03wk-2: 리스트는 쓰레기인가?

Author

최규빈

Published

March 22, 2024

1. 강의영상

2. Imports

import numpy as np
import matplotlib.pyplot as plt 

3. 리스트는 쓰레기인가?

A. 벡터화연산(브로드캐스팅) 불가능

- 벡터화연산 불가능 (최악의 단점)

lst = [1,2,3]
lst + 1
TypeError: can only concatenate list (not "int") to list
arr = np.array([1,2,3])
arr + 1
array([2, 3, 4])

B. bool을 이용한 인덱싱이 불가능

- True, False 가 포함된 array를 이용한 인덱싱이 불가능하다.

- 넘파이에서 bool을 이용한 인덱싱

arr = np.array([1,2,3,4,5])
arr[arr>3]
array([4, 5])

- 리스트는 불가능

lst = [1,2,3,4,5]
lst[lst>3]
TypeError: '>' not supported between instances of 'list' and 'int'
lst>3
TypeError: '>' not supported between instances of 'list' and 'int'

C. 넘파이 특화 메소드 사용불가능

- 넘파이 특화 메소드

arr = np.array([5,4,3,2,1])
arr
array([5, 4, 3, 2, 1])
arr.min(), arr.max(), arr.mean(), arr.argmin(), arr.argmax()
(1, 5, 3.0, 4, 0)
arr.sort()
arr
array([1, 2, 3, 4, 5])

- 리스트는?

lst = [5,4,3,2,1]
lst
[5, 4, 3, 2, 1]
lst.min(), lst.max(), lst.mean(), lst.argmin(), lst.argmax()
AttributeError: 'list' object has no attribute 'min'
lst.sort()
lst
[1, 2, 3, 4, 5]

# 메소드란? – 자료형에 종속된 특수기술

# 넘파이 특수기술
arr = np.array([1,2,3])
arr.max() # max(arr)로 해석
3
# ?.max() 이런건 넘파이 특수기술 이니까 ?자리에 리스트일 경우는 실행안됨
lst = [1,2,3]
lst.max() 
AttributeError: 'list' object has no attribute 'max'
# 리스트의 특수기술 
lst = [1,2,3]
lst.append(4) # append(lst,4) 로 해석
lst
[1, 2, 3, 4]
# 문자열 특수기술
s = 'asdf'
s.capitalize() # capitalize(s) 로 해석
'Asdf'
# 문자열 특수기술
s = '-'
lst = ['x','y','z']
s.join(lst) # join(s,lst) 로 해석
'x-y-z'
  • join(s,lst)의 뜻: lst의 각 원소를 모두 “이어서” 하나의 문자열로 만들어라. 단, 원소간의 구분은 s에 저장된 문자열로 하라.
# 문자열 특수기술 응용
''.join(['x','y','z'])
'xyz'
# 문자열 특수기능 
s = "제 이름은 {} 입니다." 
name = "최규빈"
s.format(name) # format(s,name)
'제 이름은 최규빈 입니다.'
  • format(s,name)의 뜻: s라는 문자열의 포맷에 {} 자리에 name을 끼워넣어라.

D. 파이썬에서 쓰레기 같은 자료형은 없다.

- 파이썬에는 각 자료형마다 사용할 수 있는 고유기술(=메소드)이나 문법이 있음.

- 내가 생각할 때 리스트는 필요없는 자료형이야 = 난 리스트만 가지고 있는 어떠한 고유특징을 활용하지 못해.

- 파이썬을 잘 하려면 자료형에 따른 고유 특징을 이해하고 활용할 줄 알아야 한다.

  • 기본자료형: str, list, tuple, dict, set – 이러한 자료형을 이해하고 기능을 활용해야함
  • 모듈: 넘파이배열, 판다스

4. 2024년 수능 – 확통 23

A. 문제파악

- 기존의 문제들과 다르게 numpy를 이용하여 풀기는 어려울 것 같다. 뭔가 기존문제들과 결이 다름

- 그런데 코딩으로 해결가능할 것 같긴 함

B. 슈도알고리즘

1. 편의상 \([x,x,y,y,z]\)\([x_1,x_2,y_1,y_2,z]\)와 같이 생각하고 나열한다. (5! = 120 개만큼 경우가 나열되겠지)

2. 중복을 제거한다. 즉 아래는 모두 같은 경우로 생각한다.

  • \([x_1,x_2,y_1,y_2,z]\)
  • \([x_1,x_2,y_2,y_1,z]\)
  • \([x_2,x_1,y_1,y_2,z]\)
  • \([x_2,x_1,y_2,y_1,z]\)

C. 예비학습

# 개념1 – 집합이라는 자료형이 있음.

a = {1,2,3}
type(a)
set

그런데 집합은 중복된 원소를 포함하지 않았음 (중학교때 배운듯)

a = {1,2,3,3,3}
a
{1, 2, 3}

#

# 개념2 – 자료형변환을 이용하여 중복된 원소를 제거

아래와 같은 자료형이 있다고 하자.

lst = [1,2,3,3,3] 
lst
[1, 2, 3, 3, 3]

중복된 것을 제외하고 싶다면?

list(set(lst)) # 자료형변환
[1, 2, 3]

#

# 개념3 – 고유의 원소 숫자 세기

lst = [1,2,3,3,3,4,4,5,5,6] 
lst
[1, 2, 3, 3, 3, 4, 4, 5, 5, 6]
len(set(lst))
6

#

# 개념4 – for문

아래를 출력하고 싶다고 하자.

이효리는 핑클의 멤버이다. 
옥주현은 핑클의 멤버이다.
성유리는 핑클의 멤버이다. 
이진은 핑클의 멤버이다. 

(풀이1) – 단순한 풀이

print("이효리는 핑클의 멤버이다.")
print("옥주현은 핑클의 멤버이다.")
print("성유리는 핑클의 멤버이다.")
print("이진은 핑클의 멤버이다.")
이효리는 핑클의 멤버이다.
옥주현은 핑클의 멤버이다.
성유리는 핑클의 멤버이다.
이진은 핑클의 멤버이다.

(풀이2) – 문자열 특수기능을 이용해볼까?

“{} 핑클의 멤버이다.” 이 공통포맷이므로, 아래와 같이 수행할 수 있겠다.

lst = ["이효리는","옥주현은","성유리는","이진은"]
i = 0
print("{} 핑클의 멤버이다.".format(lst[i]))
i = 1 
print("{} 핑클의 멤버이다.".format(lst[i]))
i = 2 
print("{} 핑클의 멤버이다.".format(lst[i]))
i = 3 
print("{} 핑클의 멤버이다.".format(lst[i]))
이효리는 핑클의 멤버이다.
옥주현은 핑클의 멤버이다.
성유리는 핑클의 멤버이다.
이진은 핑클의 멤버이다.

(풀이3) - for와 문자열 특수기능을 이용해볼까?

lst = ["이효리는","옥주현은","성유리는","이진은"]
for i in [0,1,2,3]:
    print("{} 핑클의 멤버이다.".format(lst[i]))
이효리는 핑클의 멤버이다.
옥주현은 핑클의 멤버이다.
성유리는 핑클의 멤버이다.
이진은 핑클의 멤버이다.

(풀이4) - for와 문자열 특수기능을 이용해볼까? (2)

[0,1,2,3]은 대충 range(4)와 비슷한 것이므로 아래와 같이 수행할 수도 있겠음.

lst = ["이효리는","옥주현은","성유리는","이진은"]
for i in range(4):
    print("{} 핑클의 멤버이다.".format(lst[i]))
이효리는 핑클의 멤버이다.
옥주현은 핑클의 멤버이다.
성유리는 핑클의 멤버이다.
이진은 핑클의 멤버이다.

(풀이5) - for와 문자열 특수기능을 이용해볼까? (3)

아래의 코드는 \(i\) 자리에 [0,1,2,3]의 원소가 번갈아 대입되며 ??????가 수행되었음.

lst = ["이효리는","옥주현은","성유리는","이진은"]
for i in [0,1,2,3]:
    ??????

아래의 코드도 \(i\) 자리에 range(4)의 원소가 번갈아 대입되며 ??????가 수행되었다고 해석가능.

lst = ["이효리는","옥주현은","성유리는","이진은"]
for i in range(4):
    ??????

아래의 코드는 어떻게 실행될까??

lst = ["이효리는","옥주현은","성유리는","이진은"]
for i in lst:
    ??????
# R과 다른 방식으로 동작하는 for문 
lst = ["이효리는","옥주현은","성유리는","이진은"]
for l in lst:
    print("{} 핑클의 멤버이다.".format(l))
이효리는 핑클의 멤버이다.
옥주현은 핑클의 멤버이다.
성유리는 핑클의 멤버이다.
이진은 핑클의 멤버이다.

#

# 개념5 – for문을 이용하여 리스트의 원소 추가하기

lst2 = [] # lst2 = list() 와 같은결과임
lst = ["이효리는","옥주현은","성유리는","이진은"]
for l in lst: 
    lst2.append("{} 핑클의 멤버이다.".format(l))
lst2
['이효리는 핑클의 멤버이다.', '옥주현은 핑클의 멤버이다.', '성유리는 핑클의 멤버이다.', '이진은 핑클의 멤버이다.']

그런데 이걸 아래와 같이 해결할 수도 있다.

lst = ["이효리는","옥주현은","성유리는","이진은"]
["{} 핑클의 멤버이다.".format(l) for l in lst] # 리스트컴프리헨션 
['이효리는 핑클의 멤버이다.', '옥주현은 핑클의 멤버이다.', '성유리는 핑클의 멤버이다.', '이진은 핑클의 멤버이다.']

#

# 개념6 – 리스트컴프리헨션

집합을 표현하는 방법에는 원소나열법과 조건제시법이 있다.

  • 원소나열법: \(\{2^0,2^1,2^2,2^3\}\)
  • 조건제시법: \(\{2^i: \text{ for } i=0,1,2,3\}\)

이중에서 조건제시법은 아래와 같이 표현할 수 있음.

  • \(\{2^i: \text{ for } i \in \{0,1,2,3\}\}\)

여기에서 \(\in\)in 으로 읽으므로, 위의 표기법을 연상하여 파이썬 코드로 바꿔보면

[2^i: for i in [0,1,2,3]]

와 같은 방식으로 리스트의 원소를 표현할 수 있을 것 같다. 위의 코드는 실행되지 않지만 아래의 코드는 실행가능하다.

[2**i for i in [0,1,2,3]]
[1, 2, 4, 8]
[2**i for i in range(4)]
[1, 2, 4, 8]

이를 응용하면

["asdf{}".format(2**i) for i in range(4)]
['asdf1', 'asdf2', 'asdf4', 'asdf8']

이것을 다시 응용하면

lst = ["이효리는", "성유리는", "옥주현은", "이진은"]
["{} 핑클의 멤버이다.".format(l) for l in lst]
['이효리는 핑클의 멤버이다.', '성유리는 핑클의 멤버이다.', '옥주현은 핑클의 멤버이다.', '이진은 핑클의 멤버이다.']

#

# 개념7 – 스트링의 인덱싱 (파이썬에서는 스트링을 array로 취급)

s = 'asdf'
s
'asdf'
s[0]
'a'
s[-1]
'f'
s[:2]
'as'

#

# 개념8 – 스트링과 리스트의 변환

s = 'asdf' 
s
'asdf'
lst = list(s)
lst
['a', 's', 'd', 'f']
''.join(lst)
'asdf'

# 개념9 – 튜플자료형

lst = [1,2,3]
lst
[1, 2, 3]
tpl = (1,2,3)
tpl
(1, 2, 3)

튜플은 (의미가 명확할때) 괄호를 생략할 수 있음

tpl = 1,2,3
tpl
(1, 2, 3)

튜플은 리스트와 매우 비슷함.

[1,2,3] + [4,5] 
[1, 2, 3, 4, 5]
(1,2,3) + (4,5)
(1, 2, 3, 4, 5)

아래도 가능

''.join(['a','b','c'])
'abc'
''.join(('a','b','c'))
'abc'

#

# 개념10 – for를 수행하는 다양한 테크닉

우리가 알고 있는 for: 리스트 비슷한 것을 만든 뒤, 그 리스트의 원소를 하나씩 뽑아가면서 어떠한 반복구문 “??????” 수행하는 것.

import itertools
for i in itertools.permutations(['a','b','c']):
    print(i)
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
for i in itertools.product(['x','y'],['a','b','c']):
    print(i)
('x', 'a')
('x', 'b')
('x', 'c')
('y', 'a')
('y', 'b')
('y', 'c')

#

D. 풀이

아래의 문제를 풀어보자..

(풀이)

len(set([''.join(i) for i in itertools.permutations(['x','x','y','y','z'])]))
30

5. 2024년 수능 – 확통29

A. 문제이해

B. 슈도알고리즘

1. 아래의 경우를 모두 나열한다.

a,b,c,d = 1,1,1,1 
a,b,c,d = 1,1,1,2 
....
a,b,c,d = 6,6,6,6

2. 조건 \(a \leq c \leq d\)\(b \leq c \leq d\) 를 동시에 만족하는 경우를 센다.

C. 예비학습

# 개념1 – for문과 튜플

lst = [['최규빈',43052,'M'],['아이유',54321,'F'],['하니',11223,'F']]
lst[0]
['최규빈', 43052, 'M']

아래의 코드가 가능하다.

for [name,student_id,sex] in lst:
    print([name,student_id,sex])
# [name,student_id,sex] = ['최규빈', 43052, 'M']
# print([name,student_id,sex])
# [name,student_id,sex] = ['아이유', 54321, 'F']
# print([name,student_id,sex])
# [name,student_id,sex] = ['하니', 11223, 'F']
# print([name,student_id,sex])
['최규빈', 43052, 'M']
['아이유', 54321, 'F']
['하니', 11223, 'F']

리스트를 튜플로 바꾼다면?

for (name,student_id,sex) in lst:
    print((name,student_id,sex))
# (name,student_id,sex) = ['최규빈', 43052, 'M']
# print((name,student_id,sex))
# (name,student_id,sex) = ['아이유', 54321, 'F']
# print((name,student_id,sex))
# (name,student_id,sex) = ['하니', 11223, 'F']
# print((name,student_id,sex))
('최규빈', 43052, 'M')
('아이유', 54321, 'F')
('하니', 11223, 'F')

for (name,student_id,sex) in lst: 대신에 for name,student_id,sex in lst: 도 가능

for name,student_id,sex in lst:
    print((name,student_id,sex))
# name,student_id,sex = ['최규빈', 43052, 'M']
# print((name,student_id,sex))
# name,student_id,sex = ['아이유', 54321, 'F']
# print((name,student_id,sex))
# name,student_id,sex = ['하니', 11223, 'F']
# print((name,student_id,sex))
('최규빈', 43052, 'M')
('아이유', 54321, 'F')
('하니', 11223, 'F')

아래와 같이 컴프리헨션으로 만들 수도 있음.

[[name,student_id,sex] for name,student_id,sex in lst]
[['최규빈', 43052, 'M'], ['아이유', 54321, 'F'], ['하니', 11223, 'F']]
[(name,student_id,sex) for name,student_id,sex in lst]
[('최규빈', 43052, 'M'), ('아이유', 54321, 'F'), ('하니', 11223, 'F')]

아래는 불가능

[name,student_id,sex for name,student_id,sex in lst]
SyntaxError: did you forget parentheses around the comprehension target? (905514563.py, line 1)

하지만 괄호를 명확하게 쓰기만 하면 만들어짐

[(name,sex) for name,student_id,sex in lst]
[('최규빈', 'M'), ('아이유', 'F'), ('하니', 'F')]
[(sex,name) for name,student_id,sex in lst]
[('M', '최규빈'), ('F', '아이유'), ('F', '하니')]
[name for name,student_id,sex in lst]
['최규빈', '아이유', '하니']

언더스코어(_)를 사용할 수도 있음.

[name for name,_,_ in lst]
['최규빈', '아이유', '하니']

언더스코어(_)를 사용시 lst[0], lst[1] 등의 원소숫자와 일치하도록 사용해야함

[name for name,_ in lst] # 이건 또 실행불가능..
ValueError: too many values to unpack (expected 2)
[name for name, *args in lst] # 이건 실행가능
['최규빈', '아이유', '하니']

# 개념2 – 조건문

아래의 리스트 원소 \(l\) 중에서 조건 \(1 < l \leq 5\)를 만족하는 원소는 모두 몇개인가?

lst = [1,2,3,4,5,6,7,8,9] 
lst
[1, 2, 3, 4, 5, 6, 7, 8, 9]

(풀이)

sum([1<l and l<=5 for l in lst])
4
sum([1<l<=5 for l in lst])
4

D. 풀이

(풀이)

lst = [1,2,3,4,5,6]
sum([a<=c<=d and b<=c<=d for a,b,c,d in itertools.product(lst,lst,lst,lst)])
196

6. 2024년 수능 – 19 (다시풀기)

A. 문제파악

B. 예비학습

- 예제: 제곱수중에서 12로 나누어서 떨어지는 수만 원소르 가지는 리스트를 만들고 싶다.

  • 제곱수: 1,4,9,16,25,36,…
  • 12로 나누어서 떨어지는 수: 36

(복습1)

12 % 4 # % 는 나머지를 계산하는 연산자, 12를 4로 나누었더니 나머지가 0
0
12 % 5 # 12 = 5*2 +2 
2

(복습2)

a = 3 # a에 2를 대입하라...
a == 2 # a에 들어있는 값이 2이인지 테스트하라..
False
a == 3
True

(풀이1) – 비어있는 리스트를 만들고, for + if 를 사용

lst = []
for i in range(1,101):
    if i**2 % 12 == 0: # i^2을 12로 나누어서 나누어 떨어진다면
        lst.append(i**2)
lst
[36,
 144,
 324,
 576,
 900,
 1296,
 1764,
 2304,
 2916,
 3600,
 4356,
 5184,
 6084,
 7056,
 8100,
 9216]

(풀이2) - if문이 포함된 리스트 컴프리헨션

[i**2 for i in range(1,101) if i**2 % 12 ==0] 
[36,
 144,
 324,
 576,
 900,
 1296,
 1764,
 2304,
 2916,
 3600,
 4356,
 5184,
 6084,
 7056,
 8100,
 9216]

C. 풀이

(풀이)

x = list(range(1,16))
f = lambda x: np.sin(np.pi/4*x)
sum([xi for xi in x if f(2+xi)*f(2-xi)<1/4])
32

7. 컴프리헨션 연습

지금까지 해결했던 다양한 문제를 넘파이의 벡터연산을 봉인하고 해결해보자.

# 예제1 – 아래를 리스트컴프리헨션을 이용하여 구현하라.

arr = np.array([1,2,3])
arr + 1
array([2, 3, 4])
arr * 2
array([2, 4, 6])

(풀이)

lst = [1,2,3]
[l+1 for l in lst]
[2, 3, 4]
[l*2 for l in lst]
[2, 4, 6]

#

# 예제2 – 아래를 리스트 컴프리헨션을 이용하여 구현하자.

n = np.arange(1,11)
an = 3*n - 2 
an
array([ 1,  4,  7, 10, 13, 16, 19, 22, 25, 28])

(풀이)

[3*n-2 for n in range(1,11)]
[1, 4, 7, 10, 13, 16, 19, 22, 25, 28]

#

# 예제3 – 아래의 코드에서 list(map(f,x)) 대신에 리스트 컴프리헨션을 이용하라.

x = np.arange(1,11)
f = lambda x: x+2 
list(map(f,x))
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

(풀이)

f = lambda x: x+2 
[f(x) for x in range(1,11)]
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

#

팁: 반복계산을 해야할 경우

  1. 벡터화연산이 지원된다면 그것을 사용한다. (numpy, pandas)
  2. 불가능할 경우 map(f,x)를 쓰거나 리스트컴프리헨션을 사용한다.
  3. 그래도 안되는 경우 for를 사용한다.