티스토리 뷰

[문제]

두 해의 월별 매출기록을 나타내는 데이터를 생성합니다. 첫 해 12개월의 실적값은 50~100 사이로 랜덤으로 생성한다. 두 번째 해의 실적값은 첫 번째 해의 해당 월 실적값에서 –20 ~ +20 사이로 랜덤하게 증감시킵니다.

[ 1월] 70 65 => -5
[ 2월] 60 50 => -10
[ 3월] 95 82 => -13
[ 4월] 77 59 => -18
[ 5월] 86 84 => -2
[ 6월] 81 101 => +20
[ 7월] 50 53 => +3

(2단계) 위의 데이터를 월별 증감값으로 오름차순 정렬하여 출력합니다.

(3단계) 증감값이 최대와 최소인 월을 찾습니다

[1단계 문제해결 방법]

첫해의 월별 매출기록을 12개 생성하여 리스트에 저장합니다. 매출 기록은 50~100 사이의 랜덤 값으로 생성합니다.

두번째 해의 월별 매출기록을 생성하기 위해서는 먼저 증감 값을 생성합니다. 예를 들어 1월이라면 첫해의 1월 정보에서 랜덤한 증감값을 더한 값으로 두 번째 해의 1월 매출액이 결정됩니다. 그런데 여기서는 증감값도 출력에 필요하므로 저장해 두는 것도 좋습니다.

자료구조를 어떻게 하는 것이 좋을까요? 년도 별로 12개씩 만들 수도 있고 매월 첫해 값과 두번재 해 값을 같이 생성하는 방법도 있습니다.

import random
year1 = [random.randint(50, 100) for _ in range(12)]
increase = [random.randint(-20, 20) for _ in range(12)]
year2 = [x + y for x, y in zip(year1, increase)]
data = zip(range(1, 13), year1, year2, increase)

이 코드는 첫해와 증감값을 랜덤하게 리스트로 생성한 후 year2를 두 리스트의 합으로 생성합니다. 이 때 zip을 써서 두 값을 차례로 짝지어 더한 것을 확인해 볼 수 있습니다. (여기서는 zip한 결과를 여러 번 쓰거나 저장할 필요없으므로 list로 만들지 않아도 됨) 또한 마지막 줄에서 월 값을 range()를 이용해 같이 zip으로 생성한 것도 볼 수 있습니다. 이 data는 제너레이터로 한번의 for 루프를 돌릴 수 있는 코드입니다.

만약 다음과 같은 컴프리헨션을 쓴다면? 결과는 12 x 12개의 리스트가 만들어지게 됩니다.

year2 = [x + y for x in year1 for y in increase)]

한편 앞의 코드와 좀 다르게 월별로 한꺼번에 데이터를 만드는 방법으로 다음과 같이 작성할 수도 있습니다. 이것은 같은 결과를 얻게 되지만 매출기록을 가진 data가 리스트여서 메모리에 저장되어 있고 여러 번 사용할 수 있습니다. 즉 위에서 data = list(data)한 것과 같은 효과를 가지겠지요?

data = []
for i in range(1, 13):
    a = random.randint(50, 100)
    b = random.randint(-20, 20)
    data.append((i, a, a+b, b))
                
for i, x, y, z in data:
	print('[{:2d}월] {:3d} {:3d} => {:+03d}'.format(i+1, x, y, z))

위의 코드에서 출력되는 결과는 다음과 같습니다.

[1월] 60 40 => -20
[2월] 85 83 => -2
[3월] 76 57 => -19
[4월] 81 70 => -11
[5월] 63 65 => 2
[6월] 85 81 => -4
[7월] 56 37 => -19
[8월] 95 77 => -18
[9월] 89 85 => -4
[10월] 72 91 => 19
[11월] 55 66 => 11
[12월] 97 87 => -10

출력을 줄맞춰서 보기좋게 바꾸면 다음과 같습니다.

for i, (x, y, z) in enumerate(zip(year1, increase, year2)):
	print('[{:2d}월] {:3d} {:3d} => {:+03d}'.format(i, x, z, y)) 
[ 1월]  60  40 => -20
[ 2월]  85  83 => -02
[ 3월]  76  57 => -19
[ 4월]  81  70 => -11
[ 5월]  63  65 => +02
[ 6월]  85  81 => -04
[ 7월]  56  37 => -19
[ 8월]  95  77 => -18
[ 9월]  89  85 => -04
[10월]  72  91 => +19
[11월]  55  66 => +11
[12월]  97  87 => -10

앞의 포스트에서 설명되었는데, {:+03d}는 정수를 부호를 붙이고 세자리로 출력하는데 0으로 남는 부분을 채우라는 뜻입니다. 

[2단계] 증감값으로 오름차순 정렬하여 동일하게 출력한다.

증감값으로 12달을 정렬하려면 어떻게 할 수 있을까요? 정렬은 sort를 이용할 수 있으나 여러 개 값을 가진 튜플의 리스트이므로 각 원소의 제일 첫번째 값으로 정렬하게 됩니다. 그러므로 순서를 바꾸거나 정렬할 열을 앞에 한번 더 붙일 수 있습니다. 이거나 또는 sorted 함수의 key 매개변수를 이용할 수 있습니다.

difflist = [(z, i, x, y, z) for i, x, y, z in data]
for x in difflist:
    print(x)
difflist.sort(reverse=True)
for x in difflist:
    print(x)
(0, 1, 90, 90, 0)
(3, 2, 85, 88, 3)
(-17, 3, 85, 68, -17)
(-8, 4, 51, 43, -8)
(9, 5, 80, 89, 9)
(14, 6, 54, 68, 14)
(-13, 7, 67, 54, -13)
(-10, 8, 80, 70, -10)
(6, 9, 64, 70, 6)
(14, 10, 85, 99, 14)
(11, 11, 86, 97, 11)
(14, 12, 92, 106, 14)
==> 증감값으로 내림차순 정렬한 후
(14, 12, 92, 106, 14)
(14, 10, 85, 99, 14)
(14, 6, 54, 68, 14)
(11, 11, 86, 97, 11)
(9, 5, 80, 89, 9)
(6, 9, 64, 70, 6)
(3, 2, 85, 88, 3)
(0, 1, 90, 90, 0)
(-8, 4, 51, 43, -8)
(-10, 8, 80, 70, -10)
(-13, 7, 67, 54, -13)
(-17, 3, 85, 68, -17)

정렬을 위한 키를 이용하는 방법은 data 리스트를 그대로 사용하면서 다음과 같이 코딩할 수 있습니다. 여기서는 앞의 포스트에서 설명된 lambda를 이용하고 있음을 알 수 있습니다. data의 각 요소 x에 대해 x[3] 값이 증감값입니다.

difflist = sorted(data, key= lambda x:x[3], reverse=True)

[3단계] 증감값이 최대와 최소인 월을 maxmin 함수를 이용해 찾는다.

다음 출력과 같이 최대감소한 월과 최대증가한 월을 출력해줍니다.

최대감소월 [ 3월]  85  68 => -17
최대증가월 [12월]  92 106 => +14

2단계의 마지막 결과처럼 정렬이 되어 있는 상태라면 문제없이 제일 앞과 제일 뒤의 월을 이용하여 출력하면 될 것입니다.

i, x, z, y = difflist[0]
print('최대증가월 [{:2d}월] {:3d} {:3d} => {:+03d}'.format(i, x, z, y))
i, x, z, y = difflist[11]
print('최대감소월 [{:2d}월] {:3d} {:3d} => {:+03d}'.format(i, x, z, y))

정렬되어 있지 않은 경우 최대, 최소 증감값을 찾는 방법을 살펴보겠습니다. 최대 최소값만 구한다면 정렬을 하는 것은 바람직하지 않을 것입니다. max와 min 함수를 이용할 수 있겠지요?

i, x, z, y = max(data, key=lambda x:x[3])
print('최대증가월 [{:2d}월] {:3d} {:3d} => {:+03d}'.format(i, x, z, y))

이렇게 하면 max 함수는 data 12개월의 월별 요소 x로부터 x[3]으로 최대인 것을 반환해 줍니다. 그 결과를 앞에서와 같이 네 개의 변수에 차례로 받아옵니다.

[연습문제] 

이제까지 한 것을 수정하여 증감율을 계산하여 증감율이 최대와 최소인 월을 찾도록 작성해 봅시다.

증감값이 아니라 증감율로 계산을 해봅시다. 증감율은 증감값/전년도매출액 * 100으로 구할 수 있고 다음과 같이 출력될 수 있습니다. 최대증감율 월은 최대증가월인 8월이 아니라 1월이 됨을 알 수 있습니다. (데이터는 랜덤생성이라 변경됨)

[ 1월]  54  67 => (+13.00 +24.1%)
[ 2월]  91  90 => (-01.00 -01.1%)
[ 3월]  75  63 => (-12.00 -16.0%)
[ 4월]  99 115 => (+16.00 +16.2%)
[ 5월]  94 107 => (+13.00 +13.8%)
[ 6월]  99  88 => (-11.00 -11.1%)
[ 7월]  99  80 => (-19.00 -19.2%)
[ 8월]  94 111 => (+17.00 +18.1%)
[ 9월]  94 108 => (+14.00 +14.9%)
[10월]  63  50 => (-13.00 -20.6%)
[11월]  95  95 => (+00.00 +00.0%)
[12월]  70  62 => (-08.00 -11.4%)
최대증가율 월 [ 1월]  54  67 =>  (+13.00 +24.1%)

이러한 결과를 얻기 위해서는 이전의 단계에 무엇을 추가해야 할까요? 우선 증감율 계산이 필요하고 증감율을 이용한 출력과 최대증가율 월 계산이 반영되어야 할 것입니다.

import random
year1 = [random.randint(50, 100) for _ in range(12)]
increase = [random.randint(-20, 20) for _ in range(12)]
year2 = [x + y for x, y in zip(year1, increase)]
rate = [y/x*100 for x, y in zip(year1, increase)]
data = zip(range(1, 13), year1, year2, increase, rate)
data = list(data)
for i, x, y, z, v in data:
	print('[{:2d}월] {:3d} {:3d} => ({:+06.2f} {:+05.1f}%)'.format(i, x, y, z, v))
i, x, z, y, v = max(data, key=lambda x:x[4])
print('최대증가율 월 [{:2d}월] {:3d} {:3d} =>  ({:+06.2f} {:+05.1f}%)'.format(i, x, z, y, v))

이와 같이 zip을 할 때 증감율 항목 rate을 하나 추가하여 생성할 수 있습니다.

그리고 출력에서도 증감율 부분을 추가하여 %를 붙여 주었습니다.

최대증가율 계산을 위해서는 key를 리스트의 각 요소의 [4]번째 값 증 증감율 값으로 max 함수를 호출하게 고쳐주어야 합니다z.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함