모듈은 함수, 변수, 클래스 등의 정의를 담고 있는 소스코드 파일이며, 파일 확장자는 .py이다.
다음 두 모듈을 이용하여 모듈 활용법을 소개한다.
random모듈: 무작위 수 생성 및 계산에 필요한 다양한 함수 제공statistics모듈: 통계 분석을 위한 다양한 함수 제공
모듈은 상황에 따라 다음 세 가지 중 하나의 방식으로 활용된다.
import 모듈명from 모듈명 import ...import 모듈명 as 모듈별칭
8.1random 모듈¶
8.1.1모듈 불러오기: import ...¶
주사위 던지기를 컴퓨터 시뮬레이션 하려면 1부터 6까지의 정수 중에 하나를 무작위로 선택하는 기능을 구현해야 한다.
이런 경우에 random 모듈의 randint() 함수를 다음과 같이 활용한다.
import random
random.randint(1, 6)6이전 코드에서 import random은 random이라는 모듈을 불러(import)와,
그 안에 정의된 함수들을 사용할 수 있도록 준비시키는 명령문이다.
따라서 이 명령문이 먼저 실행되어야만 randint() 같은 함수를 호출할 수 있다.
반면에 round(), print() 같은 함수는 파이썬이 실행될 때
기본적으로 제공되는 내장 함수(built-in function)이므로
별도의 모듈을 불러오지 않아도 바로 사용할 수 있다.
모듈 불러오기는 한 번만 하면 된다. 대신 모듈에 포함된 함수는 항상 모듈 이름과 함께 아래 형식으로 호출해야 한다.
모듈.함수(인자1, 인자2, ...)8.1.2random.randint() 함수¶
아래 코드는 random.randint(1, 6)를 5번 실행한다.
그런데 함수의 반환값이 실행할 때마다 임의로 달라지는 것처럼 보인다.
실제로 random.randint() 함수는 호출될 때마다 두 인자로 지정된 구간에서
무작위로 하나의 값을 반환한다.
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randint(1, 6))4
3
2
5
2
8.1.3random.randrange() 함수¶
random.randrange() 함수는 1개에서 최대 3개 까지의 인자를 받으며
인자의 개수에 따라 기능이 조금씩 달라진다.
random.randrange(n)
random.randrange() 함수는 하나의 양의 정수 n과 함께 호출되면
0부터 n-1 까지의 정수 중에서 무작위로 하나의 정수를 반환한다.
따라서 0부터 5까지의 정수 중에서 하나를 무작위로 생성하려면
6을 인자로 사용해서 호출한다.
print(random.randrange(6))
print(random.randrange(6))
print(random.randrange(6))
print(random.randrange(6))
print(random.randrange(6))4
5
4
5
0
random.randrange(n, m)
random.randrange() 함수를 두 개의 양의 정수 n, m과 함께 호출하면
n부터 m-1 까지의 정수 중에서 무작위로 하나의 정수를 반환한다.
예를 들어 1부터 6까지의 정수 중에서 하나를 무작위로 생성하려면
1과 7을 인자로 사용해서 호출해야 한다.
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.randrange(1, 7))5
2
3
4
4
random.randrange(n, m, s)
random.randrange(n, m, s) 형식으로 함수 호출이 되면
n에서 m-1까지의 구간에서 아래에 언급된 값들 중에서 하나를 무작위로 선택한다.
n, n+s, n+2*s, n+3*s, ...예를 들어 random.randrange(1, 10, 2)는 1부터 9까지의 홀수 중에서 무작위로 하나를 선택한다.
print(random.randrange(1, 10, 2))
print(random.randrange(1, 10, 2))
print(random.randrange(1, 10, 2))
print(random.randrange(1, 10, 2))
print(random.randrange(1, 10, 2))9
5
5
3
5
반면에 random.randrange(0, 101, 5)는 0부터 100까지의 5의 배수 중에서 무작위로 하나를 선택한다.
print(random.randrange(0, 101, 5))
print(random.randrange(0, 101, 5))
print(random.randrange(0, 101, 5))
print(random.randrange(0, 101, 5))
print(random.randrange(0, 101, 5))0
55
75
5
50
randrange() vs. randint()
두 함수의 관계는 다음과 같다.
random.randint(a, b) = random.randrange(a, b+1)따라서 random.randint(a, b)로 호출되면 무작위로 선택할 수 있는 정수의 구간이
a에서 b까지이다.
반면에 random.randrange(a, b)로 호출되면 b는 포함되지 않는다.
예를 들어 아래 코드는 실행할 때마다 1 또는 2가 반환값으로 지정된다.
print(random.randint(1, 2))
print(random.randint(1, 2))
print(random.randint(1, 2))
print(random.randint(1, 2))
print(random.randint(1, 2))2
1
2
2
2
반면에 아래 코드는 항상 1만 생성된다. 이유는 탐색 구간에 2는 포함되지 않기 때문이다.
print(random.randrange(1, 2))
print(random.randrange(1, 2))
print(random.randrange(1, 2))
print(random.randrange(1, 2))
print(random.randrange(1, 2))1
1
1
1
1
8.1.4random.random() 함수¶
random 모듈의 이름과 동일한 이름을 가진 random() 함수는 구간 [0, 1), 즉 0 이상, 1 미만의 구간에서 무작위로 하나의 부동소수점을
반환한다.
아래 코드는 random() 모듈을 세 번 호출할 때마다 매번 무작위로 새로운 부동소수점이 생성됨을 보여준다.
print(random.random())
print(random.random())
print(random.random())0.320394328569893
0.17993252065954546
0.05939445018326883
random.random() 함수는 인자를 받지 않고 무조건 0 이상, 1 미만의 구간에서 하나의 부동소수점을 무작위로 선택해서 반환한다.
그렇지만 다른 구간에서도 부동소수점을 무작위로 선택할 수 있도록 할 수 있다.
예를 들어, 아래 코드는 0 이상, 10 미만의 구간에서 부동소수점을 임의로 선택한다.
random.random() * 103.2527124017395637반면에 아래 코드는 100 이상, 110 미만의 구간을 대상으로 한다.
random.random() * 10 + 100102.148973674182748.1.5시드 값¶
random.randint(), random.random(), random.randrange() 등 random 모듈의 함수는
호출될 때마다 무작위로 값을 생성하는 것처럼 보이지만,
실제로는 컴퓨터가 정해진 규칙(의사난수 알고리즘)에 따라
숫자들을 만들어내는 것이다.
random.seed() 함수는 이 규칙의 출발점을 지정하는 역할을 한다.
따라서 같은 시드seed 값을 주면 항상 같은 순서의 난수들이 생성되며,
실행할 때마다 동일한 결과를 얻을 수 있다.
아래 두 셀 모두 random.seed() 함수와 함께 random 모듈의 함수를 호출하면 항상 동일한 결과를 얻는다.
아래 코드는 random.seed() 함수가 호출될 때마다 시드 값이 재설정되어 동일한 결과가 나온다는 사실을 보여준다.
print("시드 설정:")
print()
random.seed(100)
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.random())
print(random.random())
print()
print("시드 재설정:")
print()
random.seed(100)
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.random())
print(random.random())시드 설정:
2
4
4
2
0.705513226934028
0.7319589730332557
시드 재설정:
2
4
4
2
0.705513226934028
0.7319589730332557
아래 코드는 시드를 재설정하지 않으면 실행할 때마다 값이 달라짐을 보여준다.
print("시드 설정:")
print()
random.seed(100)
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.random())
print(random.random())
print()
print("시드 재설정 없음:")
print()
# random.seed(100)
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.random())
print(random.random())시드 설정:
2
4
4
2
0.705513226934028
0.7319589730332557
시드 재설정 없음:
4
5
1
5
0.12131185889358598
0.7367112819548389
시드 값을 변경하면 다른 결과를 얻는다.
print("시드 설정:")
print()
random.seed(17)
print(random.randint(1, 6))
print(random.randint(1, 6))
print(random.randrange(1, 7))
print(random.randrange(1, 7))
print(random.random())
print(random.random())시드 설정:
5
4
3
3
0.2896253777644655
0.7661074377979527
8.1.6예제¶
예제 1
random 모듈의 randint() 함수를 이용하여 주사위 던지기를
시뮬레이션 하는 코드를 구현하라.
답 1:
주사위는 1부터 6까지의 숫자로 표기된 6개의 면을 갖는 정육면체다.
주사위 자체는 생략하고 주사위를 던진 결과만을 고려한다면
1부터 6까지의 정수 중에 임의로 하나의 값을 선택하는
randint(1, 6) 함수 표현식으로 주사위를 대신할 수 있다.
즉, 아래 코드가 주사위 던지기를 시뮬레이션 한다.
import random
print("주사위를 던집니다.")
dice_number = random.randint(1, 6)
print(dice_number, "이(가) 나왔습니다.")주사위를 던집니다.
6 이(가) 나왔습니다.
답 2:
반면에 정육면체 주사위까지는 아니지만 주사위를 던졌을 때 윗쪽면에 보여지는 숫자 이미지를 모방하는 문자열을 이용하여 주사위 던지기를 아래처럼 시뮬레이션 할 수 있다.
1 2 3 4 5 6
| | |0 | |0 | |0 0| |0 0| |0 0|
| 0 | | | | 0 | | | | 0 | |0 0|
| | | 0| | 0| |0 0| |0 0| |0 0|아래 prince_dice() 함수는 지정된 숫자의 이미지를 출력한다.
def print_dice(dice_number):
if dice_number == 1:
print("| |")
print("| 0 |")
print("| |")
if dice_number == 2:
print("|0 |")
print("| |")
print("| 0|")
if dice_number == 3:
print("|0 |")
print("| 0 |")
print("| 0|")
if dice_number == 4:
print("|0 0|")
print("| |")
print("|0 0|")
if dice_number == 5:
print("|0 0|")
print("| 0 |")
print("|0 0|")
if dice_number == 6:
print("|0 0|")
print("|0 0|")
print("|0 0|")1에 해당하는 이미지
print_dice(1)| |
| 0 |
| |
6에 해당하는 이미지
print_dice(6)|0 0|
|0 0|
|0 0|
위 함수를 이용하여 주사위 던지기를 아래 코드로 구현할 수 있다. 아래 코드는 실행될 때마다 무작위로 1부터 6 사이의 값에 해당하는 이미지를 출력한다.
import random
print("주사위를 던집니다.")
print()
dice_number = random.randint(1, 6)
print_dice(dice_number)주사위를 던집니다.
|0 0|
| 0 |
|0 0|
import random
print("주사위를 던집니다.")
print()
dice_number = random.randint(1, 6)
print_dice(dice_number)주사위를 던집니다.
|0 0|
|0 0|
|0 0|
예제 2
이전 예제의 주사위 던지기 코드를 수정하여
사용자로부터 Y 또는 y를 입력받는 동안
주사위 던지기를 반복하는 프로그램을 구현하라.
또한 다른 키를 누르면 프로그램은 바로 종료되어야 한다.
힌트: while 반복문, input() 함수, 문자열 in 연산자 활용
답:
이전 코드는 주사위를 한 번 던지는 것을 구현한다.
그리고 사용자로부터 입력받은 문자열이 Y 또는 y인 동안 반복해서 주사위를 던지려면
이전 코드를 while 반복문의 본문으로 사용한다.
while 논리식에 사용되는 논리식은
사용자가 입력한 알파벳이 'Y' 또는 y인지 여부를 판단해야 한다.
예를 들어 rolling 변수가 사용자의 입력값을 가리킨다고 가정할 때
다음과 같이 while 반복문을 시작할 수 있다.
while rolling in 'Yy':
본문또한 while 반복문의 본문에서 주사위 한 번 던지기가 완료되면
곧바로 주사위를 계속해서 던질지 여부를 묻고
그 결과를 rolling 변수에 재할당한다.
그러면 사용자의 답변 여부에 따라 주사위 던지기를 반복할지가 결정된다.
위 설명을 정리하면 다음과 같이 주사위 반복 던지기를 구현할 수 있다.
import random
rolling = input("주사위 던지기를 시작하려면 Y를 누르세요! ")
while rolling in "Yy":
print("주사위를 던집니다.")
print()
dice_number = random.randint(1, 6)
print_dice(dice_number)
print()
rolling = input("계속하고 싶으면 Y를 누르세요: ")주사위를 던집니다.
|0 |
| 0 |
| 0|
주사위를 던집니다.
| |
| 0 |
| |
예제 3
random 모듈의 randrange() 함수, 따라서 randint() 함수는
지정된 정수 구간에서 균등한 확률로 하나의 정수를 선택한다.
(1)
randint(1, 6)을 10만 번 호출했을 때
1이 선택되는 경우가 전체의 1/6, 즉 0.1666 정도임을 확인하는 코드를 작성하라.
단, for 반복문과 range() 함수를 활용해야 한다.
답:
아래 코드는 random.randint(1, 6)를 10만 번 호출해서 반환값이 1인 비율을 계산한다.
total: 주사위 던직 횟수count: 주사위 던지기 결과로 1이 나온 횟수
import random
total_count = 100_000 # 주사위 던지기 횟수: 10만
count = 0 # 1이 나온 횟수
for toss in range(total_count):
if random.randint(1, 6) == 1:
count += 1
ratio = count / total_count # 1이 나온 비율
print("1이 나온 비율:", ratio)1이 나온 비율: 0.16889
(2)
이전 코드를 수정하여 주사위 던지기 횟수(total_count)를 인자로 받아 호출되면
1이 나온 비율을 반환하는 toss_ratio() 함수를 구현하라.
답:
이전 코드에서 전역 변수로 선언된 total_count를
정의되는 함수의 매개 변수로,
그리고 비율을 가리키는 지역 변수 ratio 변수를 반환값으로 지정하면 된다.
import random
def toss_ratio(total_count):
count = 0 # 1이 나온 횟수
for toss in range(total_count):
if random.randint(1, 6) == 1:
count += 1
ratio = count / total_count # 1이 나온 비율
return ratio아래 코드는 10만번 주사위 던져서 1이 나온 비율 반환한다.
toss_ratio(100_000)0.16542(3)
주사위 던지기 횟수(total_count)와 함께 1부터 6 사이의 정수(target)를
인자로 받아 호출되면 지정된 정수가 나온 비율을 반환하도록
toss_ratio() 함수를 수정하라.
또한, target의 기본 키워드 인자로 1을 지정하고,
주사위를 10만번 던질 때 1과 3이 나오는 비율 모두 1/6 정도임을 확인하라.
답:
이전 코드에서 정의된 toss_ratio() 함수의 본문에서 사용된 정수 1이
target 역할을 수행한다.
따라서 1 대신 target을 함수의 인자로 받도록 수정하면 된다.
또한 기본 키워드 인자로 1을 지정한다.
import random
def toss_ratio(total_count, target=1):
count = 0 # target이 나온 횟수
for toss in range(total_count):
if random.randint(1, 6) == target:
count += 1
return count / total_count아래 코드는 주사위를 10만번 던졌을 때 1이 나온 비율을 반환한다.
toss_ratio(100_000)0.16532아래 코드는 주사위를 10만번 던졌을 때 3이 나온 비율을 반환한다.
toss_ratio(100_000, target=3)0.16745예제 4
베스킨라빈스 31 게임은 두 사람이 참여하며 마지막에 31을 부른 사람이 지는 게임으로 규칙은 다음과 같다.
참여자들은 번갈아가며 1부터 31까지의 수를 순서대로 부른다.
한번에 1~3개의 수를 부를 수 있다.
예를 들어, playerA가 1, 2, 3을 부르면, playerB는 4 또는 4, 5 또는 4, 5, 6을 부를 수 있다.
(1) playerA와 playerB가 번갈아서 1에서 3 사이의 수를 입력하면 그만큼의 정수가 차례대로 화면에 출력되도론 한 다음에 31을 언급한 사람이 지면서 게임이 종료되도록 하는 코드를 작성하라.
답:
아래 코드의 주석을 활용하여 코드의 작동과정을 이해할 수 있다.
import random
final = 31 # 언급되면 지는 숫자
call = 0 # 마지막으로 언급된 숫자 기억
player = 0 # 숫자를 불러야 하는 게임 참여자. 0은 playerA, 1은 playerB를 가리킴.
# call이 final보다 작은 동안 게임 지속
while call < final:
# 플레이어 이름과 다음 플레이어 설정
if player == 1:
player_name = 'playerB'
player = 0
else:
player_name = 'playerA'
player = 1
print(player_name + " 차례입니다.")
# 플레이어가 부를 숫자의 개수 입력
number = int(input("부를 숫자의 개수를 입력하세요 (1, 2, 3만 입력 가능): "))
# 플레이어가 부르는 숫자 출력
count = 0
while count < number:
call += 1
print(player_name+":", call)
if call == final:
print(player_name+": 졌습니다")
break
count += 1
print()playerA 차례입니다.
playerA: 1
playerA: 2
playerA: 3
playerB 차례입니다.
playerB: 4
playerB: 5
playerB: 6
playerA 차례입니다.
playerA: 7
playerA: 8
playerA: 9
playerB 차례입니다.
playerB: 10
playerB: 11
playerB: 12
playerA 차례입니다.
playerA: 13
playerA: 14
playerB 차례입니다.
playerB: 15
playerB: 16
playerB: 17
playerA 차례입니다.
playerA: 18
playerA: 19
playerA: 20
playerB 차례입니다.
playerB: 21
playerB: 22
playerB: 23
playerA 차례입니다.
playerA: 24
playerA: 25
playerB 차례입니다.
playerB: 26
playerB: 27
playerB: 28
playerA 차례입니다.
playerA: 29
playerB 차례입니다.
playerB: 30
playerB: 31
playerB: 졌습니다
(2) 사람 대 컴퓨터의 베스킨라벤스 31 게임을 구현하라. 단, 사람이 먼저 시작하고 컴퓨터는 임의로 1~3개의 수를 부르도록 한다. 또한 사람이 항상 이기는 전략이 있는지 확인한 후에 해당 전략을 활용하여 게임을 진행하라.
답:
숫자를 부를 차례가 사람이면 몇 개를 부를지 지정하라 하고
컴퓨터 차례이면 부를 숫자의 개수를 random.randint() 함수를 이용하여
1에서 3 사이에서 무작위로 선택하도록 한다.
승리 전략:
사람이 playerA, 컴퓨터가 playerB 라고 가정한다.
그러면 사람이 항상 4로 나눌 때 나머지가 2인 수까지만 부르는 전략을 따르면 항상 이긴다.
이유는 다음과 같다.
컴퓨터가 31을 부르도록 하기 위해 사람이 30을 불러야 한다.
컴퓨터가 30을 부르지 못하도록 하기 위해 사람이 26을 불러야 한다.
컴퓨터가 26을 부르지 못하도록 하기 위해 사람이 22을 불러야 한다.
...
컴퓨터가 6을 부르지 못하도록 하기 위해 사람이 2을 불러야 한다.
사람이 처음 시작할 때 1과 2를 부른다.
아래 코드를 실행할 때 사람이 먼저 1과 2를 부르고 앞서 설명한 전략을 이용하여 컴퓨터가 부르는 숫자의 개수에 대응하도록 하면 반드시 컴퓨터가 지게 됨을 확인할 수 있다. 즉, 사람은 어떤 경우에도 2, 4, 8, ..., 26, 30을 부르면 반드시 게임을 이긴다.
import random
final = 31 # 언급되면 지는 숫자
call = 0 # 마지막으로 언급된 숫자 기억
player = 0 # 숫자를 불러야 하는 게임 참여자. 0은 playerA, 1은 playerB를 가리킴.
while call < final:
if player == 1:
player_name = 'playerB'
number = random.randint(1, 3)
3
3
2
3
player = 0
else:
player_name = 'playerA'
number = int(input("부를 숫자의 개수를 입력하세요 (1, 2, 3만 입력 가능): "))
player = 1
print(player_name + " 차례입니다.")
count = 0
while count < number:
call += 1
print(player_name+":", call)
if call == final:
print(player_name+": 졌습니다.")
break
count += 1
print()playerA 차례입니다.
playerA: 1
playerA: 2
playerB 차례입니다.
playerB: 3
playerA 차례입니다.
playerA: 4
playerA: 5
playerA: 6
playerB 차례입니다.
playerB: 7
playerB: 8
playerB: 9
playerA 차례입니다.
playerA: 10
playerB 차례입니다.
playerB: 11
playerB: 12
playerB: 13
playerA 차례입니다.
playerA: 14
playerB 차례입니다.
playerB: 15
playerB: 16
playerB: 17
playerA 차례입니다.
playerA: 18
playerB 차례입니다.
playerB: 19
playerB: 20
playerB: 21
playerA 차례입니다.
playerA: 22
playerB 차례입니다.
playerB: 23
playerB: 24
playerB: 25
playerA 차례입니다.
playerA: 26
playerB 차례입니다.
playerB: 27
playerB: 28
playerB: 29
playerA 차례입니다.
playerA: 30
playerB 차례입니다.
playerB: 31
playerB: 졌습니다.
8.1.7연습문제¶
문제 1
1에서 99까지의 정수 중에서 3의 배수를 무작위로 세 개 생성하는 코드를 작성하라.
힌트: random.randrange() 함수와 for 반복문 활용.
문제 2
아래 코드는 사용자가 1부터 100 사이의 정수 하나를 입력하여
비밀인 secret 변수에 할당된 값을 맞히는 게임 프로그램이다.
단, 정답을 입력하지 못하면 정답에 대한 힌트를 전달하여
사용자가 보다 좁은 범위에서 입력값을 정하도록 반복해서 유도한다.
print("수 알아맞히기 게임에 환영합니다.")
secret = 17
guess = -1 # 이어지는 while 반복문이 최소 한 번은 실행되도록 함
while guess != secret:
guess = int(input("1부터 100 사이의 정수 하나를 입력하세요: "))
if guess == secret:
print("맞았습니다!")
elif guess > secret:
print("너무 커요!")
else:
print("너무 작아요!")
print("게임 종료!")수 알아맞히기 게임에 환영합니다.
1부터 100 사이의 정수 하나를 입력하세요: 30
너무 커요!
1부터 100 사이의 정수 하나를 입력하세요: 10
너무 작아요!
1부터 100 사이의 정수 하나를 입력하세요: 17
맞았습니다!
게임 종료!
그런데 비밀이 17로 고정되어 있어서 한 번 답을 맞히면 더 이상 재미가 없다.
따라서 프로그램을 실행할 때마다 secret에 할당되는 값을 random 모듈의 randint() 함수를
이용하여 지정하도록 위 코드를 수정하라.
문제 3
random 모듈의 uniform() 함수가 random.uniform(a, b) 형식으로 호출되면
구간 a 이상, b 미만의 구간에서 무작위로 하나의 부동소수점을
선택해서 반환한다.
실제로 random.uniform(a, b)는 다음과같이 정의되었다.
random.uniform(a, b) = a + (b - a) * random.random()uniform이란 단어의 의미는 지정된 구간에서 균등한 확률로 부동소수점을 선택한다는 의미이다.
이를 확인하기 위해 random.uniform(0, 1)을 10만 번 호출했을 때
0.1 보다 작은 값이 나온 경우가 10% 정도임을 확인하는 코드를 작성하라.
힌트: for 반복문, range() 함수, random.uniform() 함수 활용
문제 4
베스킨라빈스 31 게임은 두 사람이 참여하며 마지막에 31을 부른 사람이 지는 게임으로 규칙은 다음과 같다.
참여자들은 번갈아가며 1부터 31까지의 수를 순서대로 부른다.
한번에 1~3개의 수를 부를 수 있다.
예를 들어, playerA가 1, 2, 3을 부르면, playerB는 4 또는 4, 5 또는 4, 5, 6를 부를 수 있다.
두 대의 컴퓨터 playerA와 playerB가 진행하는 베스킨라벤스 31 게임을 다음과 같이 구현할 수 있다.
아래 코드의 beskinRobbins31() 함수를 실행하면
playerA와 playerB 모두 승리 전략이 없이 무작위로 숫자를 부르고 31을 부르는 컴퓨터가 결정되면
해당 컴퓨터 이름을 반환한다.
원래 각 컴퓨터가 부르는 숫자를 화면에 출력해야 게임이 진행되는 과정을 볼 수 있지만 생략하고 최종 결과만 보여주도록 하였다.
import random
def beskinRobbins31():
final = 31 # 언급되면 지는 숫자
call = 0 # 마지막으로 언급된 숫자 기억
player = 0 # 숫자를 불러야 하는 게임 참여자. 0은 playerA, 1은 playerB를 가리킴.
while call < final:
if player == 1:
player_name = 'playerB'
else:
player_name = 'playerA'
number = random.randint(1, 3)
count = 0
while count < number:
call += 1
if call == final:
return player_name # 게임에 진 사용자 반환
count += 1
player = (player + 1) % 2print(beskinRobbins31() + "가 졌습니다.")playerB가 졌습니다.
print(beskinRobbins31() + "가 졌습니다.")playerA가 졌습니다.
beskingRobbins31() 게임을 십만 번 진행했을 때 각 컴퓨터의 승률를 확인하는 코드를 작성해서 두 참여자 각각 50%의 승률을 가짐을 보여라.
8.2statistics 모듈¶
statistics 모듈은 파이썬 표준 라이브러리에 포함된 기초 통계 계산 도구이다.
평균, 중앙값, 최빈값, 분산, 표준편차 등 기본적인 통계량을 쉽게 계산할 수 있도록 함수들을 제공한다.
데이터 분석이나 수학적 계산을 시작하는 입문 단계에서 유용하게 활용할 수 있다.
8.2.1모듈 별칭 지정: import ... as ...¶
statistics 모듈은 보통 아래 코드에서처럼 stats 라는 별칭으로 불러온다.
import statistics as stats8.2.2statistics 모듈의 주요 함수¶
statistics 모듈이 제공하는 주요 함수는 다음과 같다.
각 함수의 인자인 data는 리스트를 사용하면 된다.
| 함수 | 기능 |
|---|---|
mean(data) | 평균값 계산 |
median(data) | 중앙값 계산 |
mode(data) | 최빈값 계산 |
stdev(data) | 표준편차 계산 |
아래 코드의 scores 변수는 7명 학생의 성적을 담은 리스트를 가리킨다.
scores = [90, 85, 85, 88, 76, 95, 90]평균값
7명의 성적 평균은 모든 점수를 더한 뒤 7로 나눈 값이다.
print("평균:", stats.mean(scores))평균: 87
중앙값
점수를 크기순으로 나열했을 때 정 가운데에 위치함 값이다.
76, 85, 85, [88], 90, 90, 95print("중앙값:", stats.median(scores))중앙값: 88
최빈값
가장 자주 등장하는 값이다. 등장 빈도가 같은 값이 여러 개면 왼쪽에 위치한 값이 선택된다.
scores가 가리키는 리스트에 85와 90 모두 두 번씩 등장한다.
하지만 90이 85보다 먼저 왼쪽에서 사용되었기에 최빈값으로 선택된다.
print("최빈값:", stats.mode(scores))최빈값: 90
표준편차
분산의 제곱근이다. 각 점수와 동일한 단위를 사용하기에 분산 보다 직관적으로 값들이 퍼진 정도를 표현한다.
print("표준편차:", stats.stdev(scores))표준편차: 5.94418483337567
8.2.3함수 불러오기: from ... import ...¶
만약 mean(), stdev() 함수를 자주 사용한다면
stats.mean(), stats.stdev()처럼
매번 stats 모듈 이름과 함께 호출하는 게 불편할 수 있다.
그런 경우 아래 코드에서처럼 자주 사용하는 함수만 따로 불러오면
다른 함수들처럼 모듈 이름을 언급하지 않으면서 활용할 수 있다.
from statistics import mean, stdev이제부터 mean()과 stdev() 함수는 내장 함수처럼 바로 호출할 수 있다.
print("평균:", mean(scores))평균: 87
print("표준편차:", stdev(scores))표준편차: 5.94418483337567
반면에 stats 모듈의 다른 함수는 항상 모델 이름과 함께 사용해야 한다.
그렇지 않으면 아래처럼
함수가 정의되지 않았다는 의미의 NameError 오류가 발생한다.
print("중앙값:", median(scores))---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[42], line 1
----> 1 print("중앙값:", median(scores))
NameError: name 'median' is not defined함수 이름 충돌 문제
from statistics import mean처럼 특정 함수만 직접 불러오면,
그 이름이 현재 사용되고 있는 다른 변수 또는 함수와 충돌할 수 있다.
예를 들어, 아래 코드는 다른 mean() 함수를 정의한다.
def mean(x):
return "내가 만든 mean 함수"그러면 아래 코드가 보여주듯이
앞서 불러온 statistics 모듈의 mean() 함수의 의미가 새로 정의된 mean() 함수에 의해 덮어쓰여진다.
mean(scores)'내가 만든 mean 함수'따라서 특정 모듈에 포함된 함수는 가급적 모듈 이름 또는 별칭과 함께 사용할 것을 권장한다.
8.2.4예제¶
아래 scores 변수는 10명의 시험 점수를 담은 리스트를 가리킨다.
scores = [72, 65, 88, 91, 77, 90, 85, 89, 95, 100]예제 1
statistics 모듈을 stats 별칭으로 불러와라.
답:
statistics 모듈의 별칭으로 stats 또는 st가 많이 사용된다.
st 별칭으로 불러오려면 다음과 같이 코드를 실행한다.
import statistics as stats예제 2
10명 성적의 평균값, 중앙값, 표준편차를 계산하라.
답:
statistics 모듈의 mean(), median(), stdev() 함수를 이용한다.
단, 모듈을 별칭으로 불러오면 반드시 별칭과 함께 함수를 호출해야 한다.
print("평균값:", stats.mean(scores))
print("중앙값:", stats.median(scores))
print("표준편차:", stats.stdev(scores))평균값: 85.2
중앙값: 88.5
표준편차: 10.768266135063508
예제 3
중앙값이 88.5로 계산되는 이유를 설명하라.
답:
먼저 리스트를 항목들의 크기 순으로 정렬하면 다음과 같다.
[65, 72, 77, 85, 88, 89, 90, 91, 95, 100]리스트 원소가 10개(짝수 개)라서 중앙에 위치한 값이 88과 89 두 개다. 중앙값은 이 둘의 평균이므로 88.5이다.
(88 + 89) / 2 = 88.5예제 4
이상치(상대적으로 매우 크거나 작은 값, outlier)이
평균값과 중앙값에 어떤 영향을 주는지 직접 확인한다.
예를 들어, 65점이 10점으로 대체된 리스트 scores_outlier를 정의한 다음에,
새로운 리스트에 포함된 성적의 평균값과 중앙값이 이전에 비해 어떻게 달라지는지 확인하라.
답:
먼저 슬라이싱과 리스트 연산을 이용하여 scores_outlier를 정의한다.
슬라이싱은 마지막 항목을 제외한 구간을 대상으로 하고, 슬라이싱 결과에 10을 마지막 항목으로 추가한다.
scores_outlier = scores[:-1] + [10] # 100을 10으로 변경
scores_outlier[72, 65, 88, 91, 77, 90, 85, 89, 95, 10]10이 이상치인 이유는 다른 값들의 최소 65점 이상인 반면에 10은 상대적으로 차이가 많이 나기 때문이다. 10명의 새 성적에 대한 평균값, 중앙값, 표준편차는 다음과 같다.
print("새 평균값:", stats.mean(scores_outlier))
print("새 중앙값:", stats.median(scores_outlier))
print("새 표준편차:", stats.stdev(scores_outlier))새 평균값: 76.2
새 중앙값: 86.5
새 표준편차: 25.098915425881565
상대적으로 많이 작은 값(10)을 넣었을 때 평균의 변화는 큰 반면에 이상치의 변화는 크지 않다. 평균값은 이상치에 민감하게 반응하는 반면에, 중앙값은 그렇지 않다.
8.2.5연습문제¶
문제 1
아래 classA와 classB는 두 반의 시험 점수를 담은 리스트이다.
classA = [82, 76, 91, 88, 95, 88, 73, 88, 90, 84]
classB = [90, 92, 45, 88, 91, 93, 89, 94, 90, 88](1) statistics 모듈을 stats 별칭으로 불러온 뒤, 두 반 각각의 평균값, 중앙값, 최빈값, 표준편차를 출력하라.
(2) 두 반의 평균값을 비교하여 평균이 더 높은 반의 이름("classA" 또는 "classB")을 출력하라. 단, if 조건문을 활용하라.
(3) 표준편차가 더 작은 반이 성적이 더 고르다고 할 수 있다. 두 반 중 성적이 더 고른 반의 이름을 출력하라.
(4) 두 리스트를 하나로 합친 total 리스트를 만든 뒤, 전체 20명의 평균값과 중앙값을 구하라. 그리고 for 반복문을 활용하여 평균 이상인 점수의 개수를 구하라.
문제 2
random 모듈과 statistics 모듈을 함께 활용하는 문제이다.
import random
import statistics as stats(1) random.seed(42)를 설정한 뒤, random.randint(50, 100)을 15번 호출하여 무작위 점수 15개를 담은 리스트 random_scores를 만들어라.
힌트: for 반복문과 리스트의 이어붙이기 연산을 활용하라.
(2) random_scores의 평균값, 중앙값, 최빈값, 표준편차를 출력하라.
(3) for 반복문을 활용하여 random_scores에서 평균 미만인 점수만 모은 리스트 below_mean을 만들고 출력하라.
(4) 모든 학생에게 5점의 보너스 점수를 주되, 100점을 초과할 수 없도록 하여 adjusted_scores 리스트를 만들어라. 그리고 보정 전후의 평균값과 표준편차를 비교하라. 단, for 반복문과 if 조건문을 활용하라.