팬더즈

From Hidden Wiki
Jump to navigation Jump to search
필독 사항 유닠스 계열 저작물, 성인물, 도박 웹 써버 보안 프로그래밍 그래핔 파싱
필독 사항 고스트BSD 표면 웹 싸이트 제작 리눅스 마스터 파이썬 트킨터 뷰티펄 숲
수학 아이투피 마약, 아청물, 해킹 웹 싸이트 보안 웹 프로그래밍 데이터 분석 게임 제작
통계학 뮤와이어 다크넽 싸이트 제작 정보 보안 기사 쟁고우 팬더즈 파이게임

개요

팬더즈 (pandas)는 효과적인 데이터 분석을 지원하는 Python 모듈입니다. pandas의 발음은 [pǽndəz]이다. (R)에서 자주 사용되는 DataFrame, 즉 엑셀(Excel) 쉬트(sheet) 같은 2차원 테이블(table) 데이터를 파이썬에서도 좀 더 파이썬스럽게 사용할 수 있습니다. 이전에는 데이터 분석한다 하면, R로만 해야하는 경우가 많았었는데, pandas가 등장한 뒤로, 파이썬에서도 손쉽게 DataFrame 데이터를 다루고, 프로그래밍할 수 있게 되었습니다. 데이터 분석분야에 R이 더 좋으냐, Python이 더 좋으냐 논란이 있기도 했습니다. 각각 장단점이 있습니다만, 객체지향적이고, 성능 더 좋고, 응용 프로그램을 만들기 좋다는 점 때문에 점점 Python을 활용한 데이터 분석이 점점 더 부각되고 있습니다.


pandas는 데이터 조작 (manipulation)과 분석 (analysis)을 위한 파이썬 라이브러리이다.

DataFrame 사용법

  • Python Pandas 기초 - 기초스러운 DataFrame 사용법

http://pinkwink.kr/735


지난번 pandas의 series[바로가기]에 이어서 다시 이번에는 DataFrame 이야기를 할려고 합니다. 이번 글도 역시 GitHub에서 발견한 예제[바로가기]를 그냥 따라하는 수준의 이야기인데요. 알고 봤더니 이게 한 책의 예제입니다.ㅠㅠ. ["Python for Data Analysis" by Wes McKinney, published by O'Reilly Media]라는 책의 예제를 저자께서 GitHub에 올린걸 무식한 PinkWink가 얼씨구나 하고 사용하는 거더군요.ㅠㅠ. 일단은 이렇게 출처를 밝히구요.ㅠㅠ. 다음에는 나름대로 가공해서 예제를 이야기해야겠어요. 이래서 출처는 꼭 적어야합니다.ㅠㅠ. 그러나.. 공개된 GitHub의 내용을 출처를 알리고 소개하므로 뭐 나름 의미있는 행동이라고 대충 생각하는 중입니다.ㅠㅠ.


소스 코드

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame = DataFrame(data)

print(frame)

하면 터미널에 아래와 같이 뜬다.

실행 결과

   pop   state  year
0  1.5    Ohio  2000
1  1.7    Ohio  2001
2  3.6    Ohio  2002
3  2.4  Nevada  2001
4  2.9  Nevada  2002


언제나 그렇듯 pandas를 import하구요. 사전형식으로 data를 선언하네요. 그 내용에는 state와 year, pop이 있네요. 그리고 이것을 DataFrame으로 선언하면 위와 같은 마치 엑셀처럼 정리가 됩니다. IPython의 저 표현이 살짝 마음에 드네요...^^ 진짜 표같아서 말이죠^^


[] 안에 들어있는 것은 리스트 자료형이다.

  • 02-3 리스트 자료형 - 점프 투 파이썬

https://wikidocs.net/14


{} 안에 들어있는 것은 딕셔너리 자료형이다.

  • 02-5 딕셔너리 자료형 - 점프 투 파이썬

https://wikidocs.net/16


내용 수정

소스 코드

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

print(DataFrame(data, columns = ['year', 'state', 'pop']))

하면 아래와 같이 뜬다.

실행 결과

   year   state  pop
0  2000    Ohio  1.5
1  2001    Ohio  1.7
2  2002    Ohio  3.6
3  2001  Nevada  2.4
4  2002  Nevada  2.9


그리고 DataFrame에서 columns 속성에서 index의 순서를 변경해 줄 수 있습니다.


소스 코드

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

print(frame2)


그 결과는 아래와 같다.

실행 결과

       year   state  pop debt
one    2000    Ohio  1.5  NaN
two    2001    Ohio  1.7  NaN
three  2002    Ohio  3.6  NaN
four   2001  Nevada  2.4  NaN
five   2002  Nevada  2.9  NaN


이번에는 0,1,2,3,4하는 인덱스를 변경한것이구요. columns에서 debt가 추가되었네요. 그러나 값은 없어서 NaN이 들어가 있군요.


소스 코드 1

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

print(frame2.year)

소스 코드 2

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

print(frame2['year'])

는 실행 결과가 같다. 아래는 실행 결과이다.

실행 결과

one      2000
two      2001
three    2002
four     2001
five     2002
Name: year, dtype: int64


특정한 열의 값을 가져와서 볼 수 있습니다.


소스 코드

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

print(frame2.ix['three'])


실행 결과

year     2002
state    Ohio
pop       3.6
debt      NaN
Name: three, dtype: object


행의 값을 확인하는 것은 ix 속성을 사용하면 됩니다.


값 추가

소스 코드

from pandas import DataFrame

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

frame2.debt = 16.5

print(frame2)


실행 결과

       year   state  pop  debt
one    2000    Ohio  1.5  16.5
two    2001    Ohio  1.7  16.5
three  2002    Ohio  3.6  16.5
four   2001  Nevada  2.4  16.5
five   2002  Nevada  2.9  16.5


아까 NaN이었던 debt에 값을 인가하는 방식이네요^^


소스 코드

from pandas import DataFrame
import numpy as np

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

frame2.debt = np.arange(5)

print(frame2)


실행 결과

       year   state  pop  debt
one    2000    Ohio  1.5     0
two    2001    Ohio  1.7     1
three  2002    Ohio  3.6     2
four   2001  Nevada  2.4     3
five   2002  Nevada  2.9     4


또 다른 방식이기도 하구요.^^


소스 코드

from pandas import DataFrame, Series

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

val = Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2.debt = val

print(frame2)


실행 결과

       year   state  pop  debt
one    2000    Ohio  1.5   NaN
two    2001    Ohio  1.7  -1.2
three  2002    Ohio  3.6   NaN
four   2001  Nevada  2.4  -1.5
five   2002  Nevada  2.9  -1.7


혹은 index를 지정하고 직접 값을 입력해도 가능하네요^^

소스 코드

from pandas import DataFrame, Series

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

val = Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2.debt = val

frame2['eastern'] = frame2.state == 'Ohio'

print(frame2)


실행 결과

       year   state  pop  debt  eastern
one    2000    Ohio  1.5   NaN     True
two    2001    Ohio  1.7  -1.2     True
three  2002    Ohio  3.6   NaN     True
four   2001  Nevada  2.4  -1.5    False
five   2002  Nevada  2.9  -1.7    False


저런 표현.. 참 재미있습니다. frame2['eastern'] = frame2.state == 'Ohio' 와 같은 표현 말이죠.^^. frame2의 state가 Ohio이면 True, 아니면 False를 eastern이라는 열에 배치하라는 것을 저렇게 한 줄로 .... 그리고 이해하기 쉽게 입력할 수 있네요.^^


기타

소스 코드

from pandas import DataFrame, Series

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}

frame2 = DataFrame(data, columns = ['year', 'state', 'pop', 'debt'],
                   index = ['one', 'two', 'three', 'four', 'five'])  

val = Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2.debt = val

frame2['eastern'] = frame2.state == 'Ohio'

del frame2['eastern']

print(frame2)


실행 결과

       year   state  pop  debt
one    2000    Ohio  1.5   NaN
two    2001    Ohio  1.7  -1.2
three  2002    Ohio  3.6   NaN
four   2001  Nevada  2.4  -1.5
five   2002  Nevada  2.9  -1.7


열 속성을 지울 수 있네요^^.


소스 코드 1

from pandas import DataFrame

pop = {'Nevada':{2001:2.4, 2002:2.9},
       'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}

frame3 = DataFrame(pop)

print(frame3)


실행 결과 1

      Nevada  Ohio
2000     NaN   1.5
2001     2.4   1.7
2002     2.9   3.6


소스 코드 2

from pandas import DataFrame

pop = {'Nevada':{2001:2.4, 2002:2.9},
        'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}

frame3 = DataFrame(pop)

print(frame3.T)


실행 결과 2

        2000  2001  2002
Nevada   NaN   2.4   2.9
Ohio     1.5   1.7   3.6


DataFrame도 numpy의 transpose처럼 행과 열을 바꿀 수 있습니다.


소스 코드 1

from pandas import DataFrame

pop = {'Nevada':{2001:2.4, 2002:2.9},
       'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}

print(DataFrame(pop, index=[2001, 2002, 2003]))


실행 결과 1

      Nevada  Ohio
2001     2.4   1.7
2002     2.9   3.6
2003     NaN   NaN


소스 코드 2

from pandas import DataFrame

pop = {'Nevada':{2001:2.4, 2002:2.9},
       'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}

frame3 = DataFrame(pop)

pdata = {'Ohio':frame3['Ohio'][:-1],
         'Nevada':frame3['Nevada'][:2]}

print(DataFrame(pdata))


실행 결과 2

      Nevada  Ohio
2000     NaN   1.5
2001     2.4   1.7


또한... 역시 numpy처럼 특정 범위만 DataFrame으로 구성가능하죠.


소스 코드 1

from pandas import DataFrame

pop = {'Nevada':{2001:2.4, 2002:2.9},
       'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}

frame3 = DataFrame(pop)

frame3.index.name = 'year'
frame3.columns.name = 'state'

print(frame3)


실행 결과 1

state  Nevada  Ohio
year               
2000      NaN   1.5
2001      2.4   1.7
2002      2.9   3.6


소스 코드 2

from pandas import DataFrame

pop = {'Nevada':{2001:2.4, 2002:2.9},
       'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}

frame3 = DataFrame(pop)

frame3.index.name = 'year'
frame3.columns.name = 'state'

print(frame3.values)


실행 결과 2

[[nan 1.5]
 [2.4 1.7]
 [2.9 3.6]]


또 열과 행 자체에 이름(name)을 지정할 수 있습니다.


소스 코드 1

from pandas import Series

obj = Series(range(3), index=['a', 'b', 'c'])
index = obj.index
print(index)


실행 결과 1

Index(['a', 'b', 'c'], dtype='object')


소스 코드 2

import pandas as pd
import numpy as np
from pandas import Series

index = pd.Index(np.arange(3))
obj2 = Series([1.5, -2.5, 0], index=index)
print(obj2.index is index)


실행 결과 2

True


소스 코드 3

import pandas as pd
import numpy as np
from pandas import Series

index = pd.Index(np.arange(3))
obj2 = Series([1.5, -2.5, 0], index=index)

print(obj2)


실행 결과 3

0    1.5
1   -2.5
2    0.0
dtype: float64


또 재미있는 표현처럼 obj2.index is index라는 것도 재미있죠. ㅎㅎ. 뭐 아무튼 그렇다는 겁니다.^^


그래프 그리기

pandas의 데이터 분석 사례를 쉬운 예제와 함께 소개해보고자 합니다.


이번에 소개할 데이터 분석 예제는 다음과 같은 성적표입니다.


성적 데이터 표
이름 국어 영어 수학 과학
1 성춘향 67 87 90 98
1 이몽룡 45 45 56 98
1 향단이 95 59 96 88
1 방자 65 94 89 98
1 월매 45 65 78 98
1 변학도 78 76 98 89
2 홍길동 87 67 65 56
2 임꺽정 89 98 78 78
2 전우치 100 78 56 65
2 일지매 99 89 87 87
2 심청 98 45 56 54
2 콩쥐 65 89 87 78


위 데이터를 갖고, 1반과 2반은 통계적으로 유의한 성적 차이가 있는지, 유의한 차이를 나타내는 과목은 어떤 과목인지, 성적 패턴이 비슷한 학생은 누구인지 등등, 저 데이터를 이용해서 알 수 있는 정보들이 많습니다.


성적표.xlsx 파일은 http://www.insilicogen.com/blog/attachment/1156365816.xlsx 에서 다운로드 받아도 되고, 위의 표를 직접 엑셀(Excel) 파일로 만들어도 된다.


잘 보셨나요? 이 예제는 간단한 성적표이지만, 실제 실무의 많은 데이터들이 이것과 비슷합니다. 생물 정보 분석한다면 가로는 유전자 혹은 유전좌위, 세로는 샘플인 데이터를 많이 다루겠지요. locus는 장소, 위치라는 뜻이며 복수는 loci이다. gene locus는 유전자의 위치(유전자 좌위, 또는 유전좌위)라는 뜻으로 쓴다. 핵심 개념과 활용 방법은 비슷합니다. 모쪼록 이 자료가 데이터 분석 실무를 수행하는데 도움이 되길 바랍니다. 다음 기회에 또 다른 유용한 통계 분석기능을 소개하겠습니다.


  • 예제로 따라해보는 Jupyter, pandas 데이터 분석

https://www.insilicogen.com/blog/197


성적 데이터 분석

  • Jupyter Notebook Viewer

https://nbviewer.jupyter.org/gist/yong27/715c0ef9a09dd6eb37e9


인코고등학교 1학년의 중간고사 성적이 나왔습니다. 1반과 2반 두개의 반으로 구성되어 있고, 학생은 각각 6명입니다. 이 성적 데이터로 간단한 데이터 분석을 하고자 합니다.


https://jupyter.org/ 에 접속하여 Jupyter Notebook의 웹 환경으로 실행하거나 파이썬의 대화형 모드로 실행해도 되지만 우리는 소스 코드를 저장해서 실행할 것이다.


우선 pandas, xlrd, NumPy, matplotlib, SciPy, scikit-learn을 설치한다.

팬더즈는 고수준 데이터 모델 (DataFrame) 모듈이다.

xlrd는 엑셀(Excel) 파일을 읽어들이는데 필요한 모듈이다. xlrd를 안 깔 경우 리눅스든 윈도우즈든 ImportError: Install xlrd >= 0.9.0 for Excel support라는 오류 메시지가 뜬다.

벡터, 행렬 데이터 전문 모듈인 넘파이를 안 깔 경우 첫번째 예제부터 ImportError: Missing required dependencies ['numpy']라는 에러 메시지가 뜨면서 안 된다. 하지만 NumPy는 pandas 설치할 때 같이 설치되므로 별도로 설치할 필요가 없다.

맽플랕립은 차트 그리기 모듈이고, 싸이파이는 통계 등 과학용 모듈이다. 싸이킽-런기계 학습(machine learning)을 위한 패키지이다.


리눅스맥OS의 경우

pip3 install pandas

와 같은 식으로 입력하면 된다. 리눅스에서 설치에 관리자(root) 권한이 필요한 경우 앞에 sudo를 붙여준다.

sudo pip3 install pandas
sudo pip3 install xlrd
sudo pip3 install matplotlib
sudo pip3 install scipy
sudo pip3 install scikit-learn

리눅스 민트의 경우 터미널에 위와 같이 입력해주면 된다.


모듈 제거는

sudo pip3 uninstall pandas

처럼 해주면 된다.


윈도우즈의 경우 시작 버튼을 누른 후 실행에서 cmd를 입력하고 Enter 키를 쳐서 Command Prompt를 실행한 후 명령 프롬프트

pip install pandas

처럼 입력해주면 된다.


그리고 아래의 코드를 메모장 등 소스 코드 편집기에 입력한다. # 뒤는 설명을 위한 주석이니까 입력할 필요 없다. 참고로 Jupyter Notebook에서 matplotlib을 띄우려면 소스 코드에 %matplotlib inline을 넣어줘야 한다.

import pandas as pd  ## 고수준 데이터 모델 (DataFrame) 모듈 pandas

df = pd.read_excel('성적표.xlsx')
print(df)

소스 코드 첫번째 줄의 import pandas as pd는 pandas 모듈을 pd라는 이름으로 불러오라는 뜻이다. df = pd.read_excel('성적표.xlsx')는 이렇게 pandas 모듈의 read_excel 함수로 불러온 자료가 df라는 변수(variable)라고 정의했다는 의미이다.

성적표.xlsx 파일이 이 파이썬 프로그램과 같은 위치에 있을 경우 운영 체제에 상관없이 '성적표.xlsx'라고 적으면 되지만 다른 위치에 있을 경우 그 위치를 적어줘야 한다.

리눅스의 경우 '/home/username/문서/성적표.xlsx'나 './문서/성적표.xlsx'는 되지만 '~/문서/성적표.xlsx'는 안 된다. /는 슬래시(slash)라고 부른다.

윈도우즈의 경우 'D:\문서\성적표.xlsx'처럼 적으면 된다. 백슬래시(backslash) 기호 \ 대신 원화 기호 ₩가 쓰이기도 한다.


만약 파일 이름이 statistics.py일 경우 리눅스의 터미널에서는 일단 이 py 파일이 있는 디렉터리(폴더)로 이동 후

python3 statistics.py

라고 입력하고, 윈도우즈의 명령 프롬프트(cmd)에서는

python statistics.py

라고 입력하여 실행한다.

위 py 파일을 실행하면 엑셀 파일을 불러옵니다. pandas의 read_excel 함수를 이용합니다. df라는 변수에 엑셀쉬트를 DataFrame으로 만듭니다.

    반   이름   국어  영어  수학  과학
0   1  성춘향   67  87  90  98
1   1  이몽룡   45  45  56  98
2   1  향단이   95  59  96  88
3   1   방자   65  94  89  98
4   1   월매   45  65  78  98
5   1  변학도   78  76  98  89
6   2  홍길동   87  67  65  56
7   2  임꺽정   89  98  78  78
8   2  전우치  100  78  56  65
9   2  일지매   99  89  87  87
10  2   심청   98  45  56  54
11  2   콩쥐   65  89  87  78

위는 실행 결과입니다. 엑셀 쉬트를 잘 읽어들였고, 또 화면에 잘 표시합니다.


import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)

print(df['총점'])

위 코드를 실행해보면

0     342
1     244
2     338
3     346
4     286
5     341
6     275
7     343
8     299
9     362
10    253
11    319
Name: 총점, dtype: int64

와 같이 뜬다.

위에서 []로 싸인 건 파이썬의 자료형 중 리스트이다. subjects라는 리스트에 '국어', '영어', '수학', '과학'이 들어있는 것이다.

02-3 리스트 자료형 - 점프 투 파이썬 https://wikidocs.net/14

pandas 모듈의 sum은 합계를 내라는 함수인데 df.sum(axis=0)은 (column), df.sum(axis=1)은 (row)의 합계를 내라는 뜻이다. NumPy의 numpy.sum 함수도 합계를 내라는 뜻인데 numpy.sum(X,axis=0)은 column을 합계내라는 것이고, numpy.sum(X, axis=1)은 row를 합계 내라는 의미이다.

Column And Row Sums In Pandas And Numpy http://blog.mathandpencil.com/column-and-row-sums

예를 들어 8행(row)인 전우치의 "국어 + 영어 + 수학 + 과학" 점수를 합계를 내보면 "100 + 78 + 56 + 65 = 299"이다.


import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)
df['평균']= df['총점'] / len(subjects)

print(df['평균'])

위의 소스 코드에서 평균을 내는 코드까지 추가해보면 아래와 같이 뜬다.

0     85.50
1     61.00
2     84.50
3     86.50
4     71.50
5     85.25
6     68.75
7     85.75
8     74.75
9     90.50
10    63.25
11    79.75
Name: 평균, dtype: float64

len()은 리스트에 들어있는 원소의 갯수가 몇 개인지 보여주는 함수이다. subjects 리스트에는 '국어', '영어', '수학', '과학'의 4개의 원소가 들어있으므로 len(subjects)는 4이다.

df['총점']을 len(subjects)로 나눈다. 8행(row)인 전우치의 299점을 4과목으로 나누면 74.75점이 나온다.

여기에 기본적인 통계인 총점과 평균을 추가해서, 평균으로 정렬해보겠습니다.

import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)
df['평균']= df['총점'] / len(subjects)
print(df.sort_values(['평균'], ascending=[False]))


ascend는 '오르다', ascending은 '오르는, 상승적인, 오름차순의' descend는 '내려가다', descending은 '내려가는, 내림차순의'라는 뜻이다. 오름차순은 "양 또는 수가 차례로 늘어가는 것", "작은 것부터 큰 것으로 가는 순서"이다. 내림차순은 "양 또는 수가 차례로 줄어가는 것", "큰 것부터 작은 것으로 가는 순서"이다. 방향은 위에서 아래, 또는 왼쪽에서 오른쪽이다.

ascending=[True]를 하면 평균 성적이 제일 낮은 이몽룡부터, 가장 높은 일지매까지 위에서 아래로 오름차순으로 정렬된다. ascending=[False]를 하면 내림차순으로 정렬하여 성적이 가장 높은 일지매가 가장 위로 올라온다.


descending=[True]를 할 경우 TypeError: sort_values() got an unexpected keyword argument 'descending'라고 오류가 뜨면서 되지 않는다. 왜냐하면 df 모듈의 sort_values 함수에는 descending이라는 인수(argument)가 없기 때문이다.


매개변수(parameter)와 인수(argument)는 혼용해서 사용되는 헷갈리는 용어이므로 잘 기억해 두기로 하자. 매개변수는 함수에 입력으로 전달된 값을 받는 변수를 의미하고 인수는 함수를 호출할 때 전달하는 입력값을 의미한다. 변수(variable)는 "숫자형, 문자열, 리스트, 튜플, 딕셔너리, 집합, 불" 자료형의 값을 저장하는 공간이다.

def sum(a, b):  # a, b는 매개변수(parameter)
    return a+b

print(sum(3, 4))  # 3, 4는 인수(argument)

아까의 소스 코드를 좀 더 알아보기 쉽게 수정해보면 다음과 같다.

import pandas

data7frame = pandas.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
data7frame['총점'] = data7frame[subjects].sum(axis=1)
data7frame['평균']= data7frame['총점'] / len(subjects)
print(data7frame.sort_values(['평균'], ascending=[False]))

즉, pandas 패키지를 불러오고, pandas 패키지의 read_excel 함수에 매개변수로 성적표.xlsl를 넣고 그걸 data7frame이라는 변수(variable)로 선언한다. 그리고 subjects 변수를 선언한다. 그 후 data7frame에 subjects를 넣고 행(row)을 더한 합계를 data7frame에 '총점'이라는 이름으로 저장한다. 그리고 마찬가지로 총점을 subjects 리스트의 원소 갯수인 4로 나눈 값을 '평균'이라는 이름으로 저장한다. 마지막으로 pandas의 패키지의 DataFrame 모듈의 sort_values 함수로 데이터를 정렬한다.

  • pandas.DataFrame.sort_values — pandas 0.22.0 documentation

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.sort_values.html

이 py 파일을 실행하면 아래와 같이 뜬다.

    반   이름   국어  영어  수학  과학   총점     평균
9   2  일지매   99  89  87  87  362  90.50
3   1   방자   65  94  89  98  346  86.50
7   2  임꺽정   89  98  78  78  343  85.75
0   1  성춘향   67  87  90  98  342  85.50
5   1  변학도   78  76  98  89  341  85.25
2   1  향단이   95  59  96  88  338  84.50
11  2   콩쥐   65  89  87  78  319  79.75
8   2  전우치  100  78  56  65  299  74.75
4   1   월매   45  65  78  98  286  71.50
6   2  홍길동   87  67  65  56  275  68.75
10  2   심청   98  45  56  54  253  63.25
1   1  이몽룡   45  45  56  98  244  61.00

일지매가 전교 1등이네요.


총점으로 막대 그래프(bar chart)를 그려볼까요? 그래프를 그리기 전, matplotlib에서 한국어가 잘 표시되기 위해 import matplotlib과 matplotlib.rc('font', family='NanumGothic')을 소스 코드에 포함합니다.


import pandas as pd
import matplotlib.pyplot as plt  ## 차트 그리기 모듈 matplotlib

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)
df['평균']= df['총점'] / len(subjects)
df.sort_values(['평균'], ascending=[False])

sorted_df = df.sort_values(['평균'], ascending=[False])
sorted_df.index = sorted_df['이름']
sorted_df['평균'].plot(kind='bar', figsize=(8, 4))

plt.show()


소스 코드 두번째 줄의 import matplotlib.pyplot as plt는 matplotlib 모듈의 pyplot 함수를 plt라는 이름으로 불러오라는 의미이다. 데비안(Debian) 계열의 리눅스에서는 family에 'NanumGothic'을 적어준다. 맥OS에서는 'AppleGothic'을 적어주면 된다. 윈도우즈 비스타 이상에서는 'Malgun Gothic'을 적어준다.

레드 햍(Red Hat) 계열의 리눅스 등 데비안 계열 외의 리눅스는 일단 나눔글꼴을 시도해 보고 안 될 경우 'Un Batang'이나 'Un Dotum'같은 은글꼴을 시도해본다.

윈도우즈 XP 이하에서는 'Dotum', 'Gulim', 'Batang', Gungsuh', 'BatangChe' 등을 시도해본다. 만약 이게 안 되면 다른 글꼴을 시도해보거나 추가로 글꼴을 설치하면 된다.


이름 끝에, '체'가 붙은 글꼴들은 '고정폭 폰트'입니다. 글자들의 간격이, 글자의 종류나 모양과 상관없이 항상 일정합니다. 프로그래밍을 할 때나, 가지런한 데이터를 작성할 때 '고정폭 폰트'를 주로 사용합니다. 따라서 일반적인 글쓰기에서는 '체'가 붙지 않은 글꼴을 사용하는 것이 자연스럽게 보입니다.

sorted_df.index = sorted_df['이름']

를 소스 코드에 넣지 않으면 그래프 아래에 사람들 이름이 안 뜨고 각 행의 숫자가 뜬다.

sorted_df['평균'].plot(kind='bar', figsize=(8, 4))

는 어떤 그래프를 보여줄지 종류와 그래프의 크기를 결정한다.

‘line’ : line plot (default). X 값이 있는 부분마다 각이 진, 선으로 그려진 그래프이다.

‘bar’ : vertical bar plot 수직 막대 그래프이다.

‘barh’ : horizontal bar plot. Y축에 사람 이름이 오고, 막대기는 수평으로 누워있는 모양이다.

‘hist’ : histogram. 테트리스처럼 생긴 히스토그램이다.

‘box’ : boxplot. 손잡이가 양쪽에 달린 주사기 모양의 boxplot이다.

‘kde’ : Kernel Density Estimation plot. 부드럽게 곡선으로 생긴 그래프이다. line에서는 일지매의 Y 값이 높은데 여기서는 낮다.

‘density’ : same as ‘kde’.라고 설명은 되어 있는데 ValueError: 'destiny' is not a valid plot kind라고 뜨면서 오류가 난다.

‘area’ : area plot. line의 밑 부분을 색으로 채워놓았다.

‘pie’ : pie plot. 동그란 원을 피자처럼 비율에 따라 나눠놓았다.

‘scatter’ : scatter plot. 각 데이터를 점으로 하나씩 찍어놓은 것이다. ValueError: plot kind 'scatter' can only be used for data frames라고 오류가 뜨면서 되지 않는다.

‘hexbin’ : hexbin plot. 이것도 ValueError: plot kind 'hexbin' can only be used for data frames라고 에러가 뜨면서 안 된다.

  • pandas.DataFrame.plot — pandas 0.22.0 documentation

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.plot.html


figsize에는 가로와 세로 크기를 넣는다.


소스 마지막 줄의 plt.show()를 코드에 넣어줘야 그래프 창이 뜬다.


자료를 평균으로 내림차순 정렬하고, 각 데이터의 이름을 이름 필드를 사용하도록 한 후, 막대그래프를 그려봅니다. 막대그래프로 평균점수를 보니까, 2위 그룹들이 비슷비슷하네요. 일지매는 방심하면 안되겠네요.

화면의 디스켓 모양 버튼(Save the figure)을 누르면 그래프를 png, jpg, pdf 파일 등으로 저장할 수 있다.


txt 파일 불러오기

엑셀 파일은 위에처럼 불러오면 되는데 텍스트 파일은 어떻게 불러오면 될까? 뷰티펄 숲에서 만든 '2017년 3월 서울 김포 공항의 평균, 최고, 최저 기온.txt' 파일로 연습해보자.


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import matplotlib
matplotlib.rc('font', family='NanumGothic')

rawData = pd.read_csv('2017년 3월 서울 김포 공항의 평균, 최고, 최저 기온.txt', names=['날짜', '평균 기온', '최고 기온', '최저 기온'], index_col='날짜')

rawData.info()

print(rawData.head(12))

rawData.plot(figsize=(10,4)).legend(ncol=4, loc=4, frameon=True)
plt.grid(True)

plt.show()


read._csv로 읽어들인다. 이 파일은 4열(column), 31행(row)의 데이터이며 ,와 엔터로 구분된 데이터이다. names로 각 열의 이름을 정해준다. info()로 이 데이터의 정보를 터미널에 보여준다. head로 12개의 행의 내용을 터미널에 보여준다.

grid로 그래포 배경에 격자(grid)를 그린다. show로 그래프를 띄운다.


Python pandas에서 text 혹은 csv 파일 읽기 http://pinkwink.kr/958


데이터 입출력 - 데이터 사이언스 스쿨 https://datascienceschool.net/view-notebook/c5ccddd6716042ee8be3e5436081778b/


pandas.read_csv — pandas 0.22.0 documentation https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html


1반과 2반의 점수 비교

그럼 이번에는 1반과 2반중 어느반이 더 잘했나 볼까요? 담임선생님들 이것에 민감하죠?

import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)

일반 = df['반'] == 1
print(일반)

와 같이 하면

0      True
1      True
2      True
3      True
4      True
5      True
6     False
7     False
8     False
9     False
10    False
11    False
Name: 반, dtype: bool

라고 뜨고

import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)

일반 = df[df['반'] == 1]
print(일반)

와 같이 하면은

   반   이름  국어  영어  수학  과학   총점
0  1  성춘향  67  87  90  98  342
1  1  이몽룡  45  45  56  98  244
2  1  향단이  95  59  96  88  338
3  1   방자  65  94  89  98  346
4  1   월매  45  65  78  98  286
5  1  변학도  78  76  98  89  341

라고 뜬다.

import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)

일반 = df[df['반'] == 1]
이반 = df[df['반'] == 2]
일반평균 = 일반['총점'].sum() / (6 * 4)
이반평균 = 이반['총점'].sum() / (6 * 4)
print(일반평균, 이반평균)

를 하면

79.04166666666667 77.125

가 뜬다.

1반이 평균이 높네요. 파이썬 3은 변수명을 한국어로 쓸 수 도 있습니다. "1반" 이라고 변수를 쓰고 싶었지만, 파이썬 변수 명명 규칙이 숫자로 시작하면 안되기 때문에 그냥 저렇게 표시합니다.

1반 선생님 좋아합니다. 그걸 보고있던 2반 선생님이 "별 차이도 안나는구먼 뭘" 이라고 합니다.

정말 두 반의 평균은 차이가 없는 걸까요? 서로 독립적인 두 집단에서 얻어진 수치형 자료로 부터 두 집단의 차이가 유의한지를 파악하는데는 t-분포(t-distribution)에 의한 t-검정(t-test)의 하나인 독립 표본 t-검정 (two independent sample t-test)이 유용합니다. SciPy의 ttest_ind 함수를 이용하여 바로 계산이 가능합니다.

import pandas as pd
import scipy.stats as stats      ## 통계 등 과학용 모듈 scipy

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)
df['평균']= df['총점'] / len(subjects)

일반 = df[df['반'] == 1]
이반 = df[df['반'] == 2]

print(stats.ttest_ind(일반['평균'], 이반['평균']))

하면

Ttest_indResult(statistic=0.319960228209846, pvalue=0.755583336185639)

가 뜬다.

p-값인 "p-value > 0.005"이므로 두 집단이 차이가 없다는 영 가설(null hypothesis)을 채택합니다. 즉 1반과 2반은 사실 유의한 점수차이가 없습니다. 오차 수준에 의한 차이라고 봐야겠네요. 1반 선생님 그렇게 좋아하시면 아니될 듯.

혹시 과목별로는 차이가 있을까요? 과목별로도 확인해보겠습니다.

import pandas as pd
import scipy.stats as stats      ## 통계 등 과학용 모듈 scipy

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)
df['평균']= df['총점'] / len(subjects)

일반 = df[df['반'] == 1]
이반 = df[df['반'] == 2]

for subject in subjects:
    print(subject, stats.ttest_ind(일반[subject], 이반[subject]))

하면

국어 Ttest_indResult(statistic=-2.490140665442242, pvalue=0.031982494983816424)
영어 Ttest_indResult(statistic=-0.6156907152631581, pvalue=0.5518533781528807)
수학 Ttest_indResult(statistic=1.4961318778859336, pvalue=0.1654958420079056)
과학 Ttest_indResult(statistic=4.328442555331755, pvalue=0.0014931977711732465)

라고 뜬다. 과학 과목은 p value < 0.005 입니다. 차이가 많이 나네요. 평균이 어떻길래.

import pandas as pd

df = pd.read_excel('성적표.xlsx')

일반 = df[df['반'] == 1]
이반 = df[df['반'] == 2]

print(일반['과학'].sum() / 6, 이반['과학'].sum() / 6)

하면

94.83333333333333 69.66666666666667

라고 뜬다. 역시 차이가 많이 났군요. 1반 학생들은 과학에 소질이 많은 것 같습니다.


과목별 비교

과목별로 다른 특징이 있는 것 같습니다. 먼저 전체 데이터를 막대 그래프(bar chart)로 그려보겠습니다. 앞서 그렸던 성적별 막대 그래프를 과목별로 각각 그려보겠습니다.

import pandas as pd  ## 고수준 데이터 모델 (DataFrame) 모듈 pandas
import matplotlib.pyplot as plt  ## 차트 그리기 모듈 matplotlib

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']
df['총점'] = df[subjects].sum(axis=1)
df['평균']= df['총점'] / len(subjects)
df.sort_values(['평균'], ascending=[False])

sorted_df = df.sort_values(['평균'], ascending=[False])
sorted_df.index = sorted_df['이름']

sorted_df[subjects].plot(kind='bar', figsize=(10, 6))

plt.show()

리눅스는 'NanumGothic'이라고 써주면 되고, 윈도우즈는 'Malgun Gothic'이라고 써주면 된다. 일지매는 전체적으로 점수가 고르게 높은데, 2등부터는 약간 들쑥날쑥하네요.

과목별 점수 분포를 볼까요? 상자 수염 그림(box-and-whisker plot, 상자 그림, box plot)으로 그려보겠습니다.

import pandas as pd  ## 고수준 데이터 모델 (DataFrame) 모듈 pandas
import matplotlib.pyplot as plt  ## 차트 그리기 모듈 matplotlib

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

df[subjects].boxplot(return_type='axes')

plt.show()

윈도우즈의 경우 'NanumGothic' 대신 'Malgun Gothic'이라고 적어줘야 한다.


1반과 2반을 각각 상자그림(boxplot)으로 그려봅시다.


1반

import pandas as pd  ## 고수준 데이터 모델 (DataFrame) 모듈 pandas
import matplotlib.pyplot as plt  ## 차트 그리기 모듈 matplotlib

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

일반 = df[df['반'] == 1]
일반[subjects].boxplot(return_type='axes')

plt.show()

윈도우즈의 경우 'Malgun Gothic'이라고 적어줘야 한다.


2반

import pandas as pd
import matplotlib.pyplot as plt

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

이반 = df[df['반'] == 2]
이반[subjects].boxplot(return_type='axes')

plt.show()

윈도우즈는 'Malgun Gothic'이라고 적어야 한다. 확실히, 1반의 과학점수가 좋네요. 분포 차이도 크지 않습니다. 대신 2반은 국어점수가 좋습니다.


과목별 상관도

보통 수학 점수가 높으면 과학 점수가 높을 것이라고 기대할 수 있습니다. 국어 점수가 높으면 영어 점수가 높을 것이라고도 기대할 수 있죠. 언어능력, 수리능력이 별개의 특징처럼 생각될 수 도 있기 때문입니다. 여기 학생들도 그런지 살펴볼까요. 산점도(scatter plot)로 그려봅시다.

import pandas as pd
import matplotlib.pyplot as plt

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

df.plot(kind='scatter', x='수학', y='과학')

plt.show()

윈도우즈는 'Malgun Gothic'이라고 적어줘야 한다.


수학 점수가 높으면 과학 점수도 높아 보입니다. 어느 정도 상관관계(correlation)가 보이네요. 얼마나 상관관계가 있을까요? 이럴 때 상관 계수(correlation coefficient)를 구해볼 수 있습니다. 피어슨 상관 계수(Pearson correlation coefficient 또는 Pearson's r)를 구해볼까요? SciPy의 stats 모듈에 함수가 있습니다.

import pandas as pd
import scipy.stats as stats      ## 통계 등 과학용 모듈 scipy

df = pd.read_excel('성적표.xlsx')

print(stats.pearsonr(df['수학'], df['과학']))

하면

(0.5632890597067751, 0.05650580486155532)

라고 뜬다. 앞 숫자가 상관 계수, 뒷 숫자가 p-값(p-value)을 의미합니다. 상관계수가 0.5보다 높으므로, 관계가 있긴 하지만 p > 0.05 이므로, 유의한 상관관계가 있지는 않네요. 데이터가 좀 더 많아지면, p-value가 의미있어지는 경우가 많습니다. 이 경우는 데이터 n수가 작아서 의미를 찾기 어려운 것 같습니다. 국어와 영어는 어떨까요?

import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as stats

import matplotlib
matplotlib.rc('font', family='NanumGothic')

df = pd.read_excel('성적표.xlsx')

print(stats.pearsonr(df['국어'], df['영어']))

df.plot(kind='scatter', x='국어', y='영어')

plt.show()

하면 터미널에는 t-검정(t-test) 결과와 p-값(p-value)인

(0.10566562777973997, 0.7437959551857836)

가 뜨고, GUI 창으로는 산점도(scatter plot)가 뜬다. 윈도우즈는 'Malgun Gothic'이라고 적어줘야 한다. 상관관계가 아주 약간 있어보이지만 별로 없네요. 이 경우도, 데이터가 많으면 좀 더 충분한 관계를 밝혀낼 수 있을 것 같습니다.


학생별 성적 패턴 분석

학생별로 어떤 학생은 수학을 잘하면서 과학을 잘하지만 영어는 약할 수 있고, 또 어떤 학생은 다른 패턴으로 잘하는 과목에 대한 특징이 있을 수 있습니다. 학생들끼리 이러한 성적 패턴이 얼마나 비슷한지 비교해 볼 수 있을까요? 이를 확인할 수 있는 다양한 방법이 있습니다. 대표적인 방법으로 주성분 분석 (principle component analysis)이 있습니다. 학생별로 4과목이라는 특성을 두개로 축약한 후, 2차원 평면에 표시하면, 그 종합적인 차이를 한눈에 알 수 있습니다.


PCA 분석은 scikit-learn이라는 라이브러리를 설치하고 수행할 수 있습니다.

PCA 분석의 입력자료는 4과목의 성적데이터 입니다.

import pandas as pd

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

print(df[subjects])

하면

     국어  영어  수학  과학
0    67  87  90  98
1    45  45  56  98
2    95  59  96  88
3    65  94  89  98
4    45  65  78  98
5    78  76  98  89
6    87  67  65  56
7    89  98  78  78
8   100  78  56  65
9    99  89  87  87
10   98  45  56  54
11   65  89  87  78

가 뜬다. 이 데이터를 정규화합니다. 전체 데이터를 최대, 최소를 기준으로 재 정리합니다. 정규화하는 함수는 scikit-learn 라이브러리에서 제공됩니다.

import pandas as pd
from sklearn.preprocessing import StandardScaler

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

X_std = StandardScaler().fit_transform(df[subjects])

print(X_std)

하면

[[-0.55809652  0.72621778  0.78954203  1.01066629]
 [-1.70024754 -1.68176748 -1.44749373  1.01066629]
 [ 0.89555023 -0.87910573  1.18431305  0.36897341]
 [-0.66192843  1.12754865  0.72374686  1.01066629]
 [-1.70024754 -0.53510783  0.          1.01066629]
 [ 0.01297899  0.09555497  1.31590339  0.43314269]
 [ 0.48022259 -0.42044187 -0.8553372  -1.68444381]
 [ 0.5840545   1.35688058  0.         -0.27271947]
 [ 1.15513001  0.21022093 -1.44749373 -1.10692022]
 [ 1.10321405  0.84088374  0.59215653  0.30480412]
 [ 1.0512981  -1.68176748 -1.44749373 -1.81278239]
 [-0.66192843  0.84088374  0.59215653 -0.27271947]]

라고 뜬다. 위 행렬 데이터가 PCA의 입력으로 사용됩니다. PCA 분석 함수는 scikit-learn 라이브러리에 있습니다.

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

X_std = StandardScaler().fit_transform(df[subjects])

pca = PCA(n_components=2)
Y = pca.fit_transform(X_std)

print(Y)

하면 터미널에

[[-1.56591653 -0.09168753]
 [ 0.11774518  2.93515218]
 [-0.20727166 -0.38845514]
 [-1.71984653 -0.24234692]
 [-1.11185524  1.70512415]
 [-1.03327136 -0.43547978]
 [ 1.88205338 -0.20323602]
 [-0.10118555 -1.27408337]
 [ 1.88020684 -0.64411706]
 [-0.39554448 -1.36047251]
 [ 2.98704605  0.37256038]
 [-0.7321601  -0.37295837]]

위와 같이 뜬다. Y 변수에 4과목(4차원)을 2차원으로 축약한 데이터 값이 저장됩니다. 보통 제1주성분, 제2주성분이라고도 합니다. 이를 원래 데이터(df)에 추가합니다.

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

X_std = StandardScaler().fit_transform(df[subjects])

pca = PCA(n_components=2)
Y = pca.fit_transform(X_std)

pca_df = pd.DataFrame(Y)
pca_df.index = df.index
pca_df.columns = ['PC1', 'PC2']
df2 = pd.concat([df, pca_df], axis=1)

print(df2)

이제 이걸 실행하면

    반   이름   국어  영어  수학  과학       PC1       PC2
0   1  성춘향   67  87  90  98 -1.565917 -0.091688
1   1  이몽룡   45  45  56  98  0.117745  2.935152
2   1  향단이   95  59  96  88 -0.207272 -0.388455
3   1   방자   65  94  89  98 -1.719847 -0.242347
4   1   월매   45  65  78  98 -1.111855  1.705124
5   1  변학도   78  76  98  89 -1.033271 -0.435480
6   2  홍길동   87  67  65  56  1.882053 -0.203236
7   2  임꺽정   89  98  78  78 -0.101186 -1.274083
8   2  전우치  100  78  56  65  1.880207 -0.644117
9   2  일지매   99  89  87  87 -0.395544 -1.360473
10  2   심청   98  45  56  54  2.987046  0.372560
11  2   콩쥐   65  89  87  78 -0.732160 -0.372958

라고 뜬다. 위 데이터의 PC1, PC2 값으로 scatter plot을 그려봅니다. 1반과 2반의 차이를 알아보기 위해, 각각을 그룹핑하고, 다른 색으로 표시합니다.

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt  ## 차트 그리기 모듈 matplotlib

df = pd.read_excel('성적표.xlsx')

subjects = ['국어', '영어', '수학', '과학']

X_std = StandardScaler().fit_transform(df[subjects])

pca = PCA(n_components=2)
Y = pca.fit_transform(X_std)

pca_df = pd.DataFrame(Y)
pca_df.index = df.index
pca_df.columns = ['PC1', 'PC2']
df2 = pd.concat([df, pca_df], axis=1)

def draw_groups(groups, loc):
    fig, ax = plt.subplots()
    ax.margins(0.1)

    for name, group in groups:
        ax.plot(group['PC1'], group['PC2'], marker='o', linestyle='', ms=8, label=name)

    ax.legend(numpoints=1, loc=loc)
    
draw_groups(df2.groupby('반'), loc='upper left')

plt.show()

하고서 그래프를 본다.

위 scatter plot에 의하면 PC1 < 0, PC2 <0 에 있는 7명의 학생이 어느 정도 비슷한 패턴을 갖는 것으로 보입니다. 이후, 교육 방침을 정하거나, 장래 희망 상담등을 할 때, 이 결과를 이용할 수 있을 것으로 보입니다.

여기까지, 성적 데이터 분석을 통한 데이터 분석 사례 소개를 마칩니다. 이 예제는 성적이지만, 실제 실무적으로 유사한 데이터가 많습니다. 이들을 분석하는데 도움이 되시길 바랍니다.


분산 분석

분산 분석(analysis of variance, ANOVA)은 분산(variance)을 분석(analysis)하는 것으로 변량 분석이라고도 불린다. 3개 이상의 처리 효과 또는 모 평균(population mean)을 비교하는 문제에서는 일반적으로 실험을 계획하고 실험을 실시한 후에 얻은 자료를 기초로 하여 문제를 분석한다.

분산 분석은 실험계획법에서 가장 많이 사용하는 분석 방법으로 특성값의 분산 또는 산포를 분석하는 방법이다. 특성값의 산포를 제곱합으로 나타내고, 이 제곱합을 실험에 관련된 요인별로 분해하며, 순수한 오차에 의한 영향보다 큰 영향을 주는 요인이 어떤 것인가를 규명하는 방법이다.


제곱합(sum of square)은 특성값의 산포이다.

처리 제곱합(treatment sum of square)은 SSt 또는 SSTreatments 또는 SSTR로 표기한다. 처리 제곱합은 요인의 변화에 따른 반응의 차에 의한 제곱합이다.

잔차 제곱합(residual sum of square)은 SSE로 표기한다. 잔차 제곱합은 아직 원인이 규명되지 않은 요인에 의한 제곱합이다.

총 제곱합(total sum of square)은 SST 또는 SSTotal 또는 TSS로 표기한다. 총 제곱합은 처리 제곱합과 잔차 제곱합의 합이다.

총 제곱합은 총 변동이고, 처리 제곱합은 집단 간 변동이며, 잔차 제곱합은 집단 내 변동이다.


일원 배치법(one-way layout)은 반복수가 같은 경우만 고려하며 특성값에 대한 한 종류의 인자의 영향을 조사하고자 할 때 사용하는 분산분석법으로서 인자의 각 수준이 처리가 되며, 2개의 처리 효과를 비교할 때는 t-검정(t-test)을 이용하나 3개 이상의 처리 효과를 비교하고자 할 때는 ANOVA 분석을 실시한다.


한 그룹 당 20명씩 3개의 그룹을 대상으로 실험을 하면 k=3이고, n=20이다.

제곱합의 자유도(degree of freedom, df)는 제곱을 한 편차의 갯수에서 편차들의 선형 제약 조건의 갯수를 차감하여 계산한다.

"제곱합의 자유도 = 제곱을 한 편차의 갯수 - 편차들의 선형 제약 조건의 갯수"이다.

총 제곱합의 자유도는 N-1 또는 nk-1이다.

처리 제곱합의 자유도는 k-1이다.

잔차 제곱합의 자유도는 N-k 또는 k(n-1)이다.

"총 제곱합의 자유도 = 처리 제곱합의 자유도 + 잔차 제곱합의 자유도"이다. 즉, (k-1) + k(n-1) = kn - 1이다.


평균 제곱(mean square, MS)은 제곱합을 자유도로 나눈 통계량이다. 처리 제곱합의 평균 제곱(MSt)은 MSt = SSt / (k-1)이다. 잔체 제곱합의 평균 제곱(MSE)은 MSE = SSE / {k(n-1)}이다.


F-비(F-ratio)는 분산 분석(ANOVA)에서 의사 결정에 필요한 검정 통계량이다.

F = MSt / MSE = [SSt / (k-1)] / [SSE / {k(n-1)}]


분산 분석표는 분석 결과를 표로 요약하여 나타낸 것으로 아래와 같다.


요인 제곱합 자유도 평균제곱 F-비 유의 확률
처리 SSt k-1 MSt = SSt / (k-1) F = MSt / MSE p
잔차 SSE k(n-1) MSE = SSE / k(n-1)
SST kn-1


k개의 처리 효과에 차이가 있는지 검정한다. 귀무 가설 H0는 α1 = α2 = ... = αk(= 0)이고, 대립 가설은 적어도 한 αi는 0이 아니라는 것(not H0)이다.

임계값(threshold)은 Fk-1, k(n-1), α이다.

의사 결정은 F > Fk-1, k(n-1), α이면 H0기각한다.

라인을 비교했을 때 "F-비(계산 값) > 임계값(Fk-1, k(n-1), α)"이면 H0를 기각한다.

면적을 비교했을 때 "유의 확률(p-value) < 유의 수준(α)"이면 H0를 항상 기각한다.


회귀 분석(regression analysis)도 분산 분석(analysis of variance, ANOVA)처럼 F-분포(F-distribution)의 F-검정(F-test)을 사용한다.


분산 분석 문제

다음 문제를 풀어보자.

세 종류의 학습 방법의 효과가 차이가 있는지 비교하기 위해 23명의 학생에게 실험을 하였다. 다음은 일원배치법을 이용한 모형을 토대로 한 분산분석표이다.


제곱합 자유도 평균제곱 F값
집단-간 800 2
집단-내 200 20
합계 1,000 22


그런데 <분산분석표>에 얼룩이 번져 결과를 알아볼 수 없게 되었다. 주어진 정보만으로 F값을 계산하고, 유의 수준 5%에서 세 학습 방법의 효과는 차이가 있는지 검정하여라. 단, 임계값 F2, 20, 0.05 = 3.49, F20, 2, 0.05 = 19.45이다.


보기

F값, 검정 결과

① 20, 세 학습 방법의 효과는 차이가 있다.

② 20, 세 학습 방법의 효과는 차이가 없다.

③ 40, 세 학습 방법의 효과는 차이가 있다.

④ 40, 세 학습 방법의 효과는 차이가 없다.

⑤ 주어진 정보만으로는 알 수 없다.







정답은 3번이다.


관련 문서