2.10 리스트와 반복문을 사용하여 계산하기

반복문은 리스트 자료형 변수를 대상으로 여러 가지 계산을 할 때 많이 사용된다. 예를 들어 10번의 시험 성적을 담고 있는 a라는 변수가 있을 때 평균 성적은 다음과 같이 구할 수 있다.

a = [90, 85, 95, 80, 90, 100, 85, 75, 85, 80]
a
[90, 85, 95, 80, 90, 100, 85, 75, 85, 80]
len(a)
10
sum = 0
for i in range(len(a)):
    sum = sum + a[i]
average = sum / len(a)
average
86.5

만약 학생이 두 명이고 이런 성적 변수가 두 개 있을 때를 생각하자. 두 학생의 시험 성적의 합은 다음과 같이 구할 수 있다.

s = []
a1 = [90, 85, 95, 80, 90, 100, 85, 75, 85, 80]
a2 = [95, 90, 90, 90, 95, 100, 90, 80, 95, 90]
for i in range(len(a1)):
    s.append(a1[i] + a2[i])
s
[185, 175, 185, 170, 185, 200, 175, 155, 180, 170]

리스트의 원소를 직접 반복문에서 사용하기

지금까지는 for 반복문을 사용할 때 range 명령으로 리스트에 적용할 인덱스 정수를 만들어서 리스트의 원소 값을 찾았다. 하지만 for 반복문에서 리스트의 원소를 직접 뽑아 카운터 변수에 넣을 수도 있다.

지금까지 반복문에서 사용해 온 range 명령은 사실 리스트를 만드는 명령이다.

list(range(5))
[0, 1, 2, 3, 4]

따라서 다음 코드는

for i in range(5):
    print(i)
0
1
2
3
4

다음 코드와 같다.

for i in [0, 1, 2, 3, 4]:
    print(i)
0
1
2
3
4

따라서 가장 위에서 보인 한 학생의 평균 계산 코드는 다음 코드와 동일하다.

sum = 0
for ai in a:
    sum = sum + ai
average = sum / len(a)
average
86.5

enumerate 함수

때로는 반복문에 정수 인덱스가 필요할 때도 있다. 예를 들어 반복문에서 append 메서드를 쓰면 계산 속도가 저하된다. 이때는 미리 저장 공간을 만들어 놓고 해당 위치에 계산 결과를 갱신해야 한다. 이때는 enumerate 명령을 쓸 수 있다. enumerate 명령은 리스트의 원소를 반복하면서 동시에 인덱스 값도 생성한다.

for i, e in enumerate(["a", "b", "c"]):
    print("i = %d, e = %s" % (i, e))
i = 0, e = a
i = 1, e = b
i = 2, e = c
s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
a1 = [90, 85, 95, 80, 90, 100, 85, 75, 85, 80]
a2 = [95, 90, 90, 90, 95, 100, 90, 80, 95, 90]
for i, (a1i, a2i) in enumerate(zip(a1, a2)):
    s[i] = a1i + a2i
s
[185, 175, 185, 170, 185, 200, 175, 155, 180, 170]

리스트의 리스트

학생이 5명이고 각 학생이 10번의 시험에 대한 성적을 가진다면 다음과 같이 리스트의 리스트로 전체 시험 성적을 나타낼 수 있다.

X = [[85,  90,  20,  50,  60,  25,  30,  75,  40,  55],
     [70, 100,  70,  70,  55,  75,  55,  60,  40,  45],
     [25,  65,  15,  25,  20,   5,  60,  70,  35,  10],
     [80,  45,  80,  40,  75,  35,  80,  55,  70,  90],
     [35,  50,  75,  25,  35,  70,  65,  50,  70,  10]]

이 때 전체 평균 점수는 다음과 같이 구한다.

sum = 0
num = 0
for i in range(len(X)):
    for j in range(len(X[i])):
        num = num + 1
        sum = sum + X[i][j]
sum / num
52.7

연습 문제 2.10.1

어떤 학생이 5개의 과목을 수강하여 다음과 같은 성적(grade)을 받았다. (4점이 만점)

\[ X = 4, 3, 2, 3, 4 \]

이 5개 과목의 이수 학점(credit hours)은 각각 다음과 같다.

\[ W = 3, 3, 1, 2, 2 \]

이 때 평균 평점(GPA)은 성적의 단순 평균이 아니라 이수 학점을 가중치(weight)로 써서 다음과 같이 가중 평균을 구해야 한다.

\[ \dfrac{\text{성적과 이수 학점을 곱한 값의 총합 즉, 가중합}}{\text{이수 학점의 총합}} = \dfrac{3 \times 4 + 3 \times 3 + 1 \times 2 + 2 \times 3 + 2 \times 4}{3 + 3 + 1 + 2 + 2}\]

\(i\)번째 과목의 성적을 \(X_i\)라고 하고 \(i\)번째 과목의 이수 학점을 \(W_i\)라고 하면 가중 평균은 다음과 같은 수식으로 나타낼 수도 있다.

\[ \dfrac{W_1 X_1 + W_2 X_2 + W_3 X_3 + W_4 X_4 + W_5 X_5}{W_1 + W_2 + W_3 + W_4 + W_5} \]

이 학생의 평균 평점을 구하는 코드를 작성한다.

연습 문제 2.10.2

자료의 분산(Variance)는 각 자료 값에서 자료의 평균값을 뺀 나머지를 제곱한 값의 평균을 말한다. 예를 들어 자료가 다음과 같다고 하자

\[ X = 6, 5, 4, 7, 3, 5 \]

이 자료의 평균은 다음과 같다.

\[ \dfrac{(6 + 5 + 4 + 7 + 3 + 5)}{6} = 5\]

각 자료 값에서 자료의 평균값을 뺀 나머지를 제곱한 값을 모두 더한 값의 평균은 다음과 같이 구한다.

\[ \dfrac{(6 - 5)^2 + (5 - 5)^2 + (4 - 5)^2 + (7 - 5)^2 + (3 - 5)^2 + (5 - 5)^2}{6} \]

이 자료의 분산을 구하는 코드를 작성한다.

zip 함수

zip 함수는 두 개의 리스트를 합쳐서 각 리스트 원소의 쌍을 원소로 가지는 하나의 리스트를 만든다. 파이썬 3에서는 명시적으로 list 명령을 사용해야 리스트가 된다.

a12 = list(zip(a1, a2))
a12
[(90, 95),
 (85, 90),
 (95, 90),
 (80, 90),
 (90, 95),
 (100, 100),
 (85, 90),
 (75, 80),
 (85, 95),
 (80, 90)]

이 때 소괄호로 표시된 묶음은 튜플(tuple)이라고 하며 리스트와 사용법이 거의 동일하다. 따라서 모든 학생의 두 과목 성적 합산을 구하는 코드는 다음과 같이 고칠 수 있다.

s = []
a1 = [90, 85, 95, 80, 90, 100, 85, 75, 85, 80]
a2 = [95, 90, 90, 90, 95, 100, 90, 80, 95, 90]
for a1i, a2i in zip(a1, a2):
    s.append(a1i + a2i)
s
[185, 175, 185, 170, 185, 200, 175, 155, 180, 170]

zip 함수를 사용할 때 리스트의 리스트 인수를 하나만 넣고 인수의 앞에 * 기호를 붙이면 리스트 쌍을 바꾼다.

a12
[(90, 95),
 (85, 90),
 (95, 90),
 (80, 90),
 (90, 95),
 (100, 100),
 (85, 90),
 (75, 80),
 (85, 95),
 (80, 90)]
a21 = list(zip(*a12))
a21
[(90, 85, 95, 80, 90, 100, 85, 75, 85, 80),
 (95, 90, 90, 90, 95, 100, 90, 80, 95, 90)]
list(zip(*a21))
[(90, 95),
 (85, 90),
 (95, 90),
 (80, 90),
 (90, 95),
 (100, 100),
 (85, 90),
 (75, 80),
 (85, 95),
 (80, 90)]

연습 문제 2.10.3

다음은 학생 이름과 점수가 들어간 리스트의 리스트 변수다.

x = [
  ["길동", 90],
  ["철수", 80],
  ["영수", 70],
  ["방자", 60],
]

복수 할당과 zip 명령을 사용하여 이 변수에서 학생 이름만 있는 변수를 만들어라.