Pandas
Pandas Cheetsheet
- 정렬
- 선택
- 결측값 색인
- 복사
- 행, 열 추가
- 통계값
- 피벗테이블 (pivot_table)
- 그룹별 통계(groupby)
- 전처리
- 시각화
import pandas as pd
import numpy as np
df = pd.read_csv('http://bit.ly/ds-korean-idol')
df.sort_index()
df.sort_index(ascending=False)
df.sort_values(by='키')
df.sort_values(by='키', ascending=False)
df.sort_values(by=['키','브랜드평판지수'], ascending=False)
df['이름']
df[:3]
df.loc[:, '이름']
df.loc[3:8, ['이름', '생년월일']]
df.iloc[:, [0,2]]
df.iloc[1:5, 1:4]
df['키'] > 180
Boolean Index로 받은 Index를 활용해서 True인 값만 색인해 낼 수 있다.
df[df['키'] < 170]
df[df['키'] > 180]['이름']
df.loc[df['키']>180, '이름']
df.loc[ df['키'] > 180, ['이름', '키']]
my_condition = ['플레디스', 'SM']
df['소속사'].isin(my_condition)
df.loc[ df['소속사'].isin(my_condition) ]
df.loc[df['소속사'].isin(my_condition), '소속사']
- info() 로 NaN 값, 즉 빠진 데이터가 어디에 있는지 쉽게 요약정보로 확인할 수 있습니다.
df.info()
- Boolean 인덱싱으로 True가 return 되는 값이 NaN이라는 것을 알 수 있습니다.
df['그룹'].isnull()
- NaN이 아닌 값에 대하여 Boolean 인덱싱
df['그룹'].notnull()
- NaN 값만 색출해내기
df['그룹'][df['그룹'].isnull()]
df.loc[df['그룹'].notnull(), ['키','혈액형']]
-
다음과 같이 복사하면 안된다.
new_df = df
new_df.head()
hex(id(new_df))
hex(id(df))
참조하고 있는 메모리 주소가 같음.
원본 데이터를 유지 시키고, 새로운 변수에 복사할 때는 copy()를 사용
copy_df = df.copy()
hex(id(copy_df))
-
dictionary 형태의 데이터를 만들어 준다음 append() 함수를 사용하여 데이터를 추가할 수 있다. 반드시, ignore_index=True 옵션을 같이 추가해 주셔야 에러가 안난다.
-
또한, append() 한 뒤 다시 df에 대입해줘야 변경한 값이 유지 됩니다.
df = df.append({'이름': '테디', '그룹': '테디그룹', '소속사': '끝내주는소속사', '성별': '남자', '생년월일': '1970-01-01', '키': 195.0, '혈액형': 'O', '브랜드평판지수': 12345678}, ignore_index=True)
df['국적'] = '대한민국'
만약, 값을 변경하고 싶다면, loc 함수를 활용해서 변경
df.loc[ df['이름']=='지드래곤', '국적'] = 'korea'
df.head()
df = pd.read_csv('https://bit.ly/ds-korean-idol')
df.describe()
산술이 가능한 수치형 데이터만 요약이 된걸 볼 수 있다.
df['키'].min()
df['키'].max()
df['키'].sum()
df['키'].mean()
df['키'].median()
df['키'].mode()
분산은 편차 제곱의 평균
표준편차는 분산의 루트
df['키'].var()
df['키'].std()
df['키'].count()
- 피벗테이블은 엑셀의 피벗테이블과 동일
- 데이터 열 중에서 두 개의 열을 각각 행 인덱스, 열 인덱스로 사용하여 데이터를 조회하여 펼쳐놓은 것을 의미
- 왼쪽에 나타나는 인덱스를 행 인덱스, 상단에 나타나는 인덱스를 열 인덱스라고 부른다
pd.pivot_table(df, index='소속사', columns = '혈액형', values='키')
aggfunc에는 추가 계산 옵션 (np.sum, np.mean) - 기본값은 평균
pd.pivot_table(df, index = '그룹', columns = '혈액형', values = '브랜드평판지수', aggfunc = np.sum)# 코드를 입력해 주세요
- groupby는 데이터를 그룹으로 묶어 분석할 때 활용한다.
groupby와 함께
- count() - 갯수
- sum() - 합계
- mean() - 평균
- var() - 분산
- std() - 표준편차
- min()/max() - 최소값, 최대값
df.groupby('소속사').count()
산술 통계는 자동으로 산술통계가 가능한 열만 출력됩니다.
df.groupby('그룹').mean()
특정 열만 출력하고 싶다면?
df.groupby('그룹')['키'].mean()
행 인덱스를 복합적으로 구성하고 싶은 경우는 인덱스를 리스트로 만들어 줍니다.
df.groupby(['혈액형','성별']).mean()
Multi-Index로 된 데이터프레임을 피벗테이블 형태로 다시 변환해 줄 수 있습니다.
df2 = df.groupby(['혈액형','성별']).mean()
df2.unstack('혈액형')
df2.unstack('성별')
reset_index() 는 Multi-Index로 구성된 데이터 프레임의 인덱스를 초기화해 줍니다
df2
df2 = df2.reset_index()
df2
fillna(): na 값에 대하여 fill해주는 함수입니다.
df['키']
df['키'].fillna(-1)
키의 NaN 값을 채워준다음 유지시키려면 inplace=True 옵션을 주거나, fillna로 채워 준 값을 다시 대입해 주어야 합니다.
df2['키'] = df2['키'].fillna(-1)
df
df.dropna()
axis=0은 행을 드랍합니다.
df.dropna(axis=0)
axis=1은 열을 드랍합니다.
df.dropna(axis=1)
df.dropna()
df.dropna(axis=0, how='any')
df.dropna(axis=0, how='all')
df['키']
df['키'].drop_duplicates()
keep 옵션으로 유지하고 싶은 데이터를 선택할 수 있습니다. keep: 'first' / 'last'
df['키'].drop_duplicates(keep='last')
df.drop_duplicates('그룹')
df.drop_duplicates(keep='last')
df.head()
drop()을 활용하여 column을 제거할 수 있습니다. column을 제거할 때는 axis=1 옵션을 줍니다.
df.drop('그룹', axis=1)
df.drop(['그룹','소속사'], axis=1)
drop()을 활용하여 row를 제거할 수 있습니다. row를 제거할 때는 제거하고자하는 index와 axis=0 옵션을 줍니다.
df.drop(3, axis=0)
복수의 row를 제거하고자 할 때는 list로 지정합니다.
df.drop([3,5], axis=0)
조건을 만족하는 행 제거
df.drop(df.loc[df['키'] > 180].index)
df2 = pd.read_csv('https://bit.ly/ds-korean-idol-2')
df_copy = df.copy()
row에 합칠 때는 pd.concat에 합칠 데이터프레임을 list로 합쳐줍니다. row 기준으로 합칠 때는 sort=False 옵션을 주어 순서가 유지되도록 합니다.
df_concat =pd.concat([df, df_copy], sort=False)
df_concat
꼬인 인덱스는 reset_index()를 이용하여 인덱스를 초기화 해줍니다.
하지만, index라는 column이 추가 됩니다. 그럴때는 drop=True 옵션으로 새로 index column이 생성되지 않도록 만들어 줍니다
df_concat.reset_index(drop=True)
column을 기준으로 합치고자 할 때는 axis=1 옵션을 부여합니다
pd.concat([df, df2], axis=1)
행의 갯수가 맞지 않는 상태에서 column concat
df3 = df2.drop([3,5])
pd.concat([df, df3], axis=1)
이전에 봤던 concat과 merge는 단순 합치는 목적과 특정 기준(index)으로 합치느냐에 따라 용도가 다릅니다.
- concat: row나 column 기준으로 단순하게 이어 붙히기
- merge: 특정 고유한 키(unique id) 값을 기준으로 병합하기
pd.merge(left, right, on='기준column', how='left')
- left와 right는 병합할 두 DataFrame을 대입합니다.
- on 에는 병합의 기준이 되는 column을 넣어 줍니다.
- how 에는 'left', 'right', 'inner', 'outer' 라는 4가지의 병합 방식중 한가지를 택합니다.
df2 에서 5개의 데이터를 고의적으로 drop한 데이터
df_right = df2.drop([1, 3, 5, 7, 9], axis=0)
df_right = df_right.reset_index(drop=True)
df_right
df
병합할 2개의 DataFrame의 행의 갯수가 맞지 않습니다. 실 사례에서도 충분히 있을 법한 일입니다.
'left' 옵션을 부여하면, left DataFrame에 키 값이 존재하면 해당 데이터를 유지하고, 병합한 right DataFrame의 값의 NaN이 대입 됩니다.
pd.merge(df, df_right, on='이름', how = 'left')
반대로 'right' 옵션을 부여하면 right DataFrame을 기준으로 병합하게 됩니다.
pd.merge(df, df_right, on = '이름', how='right')
- inner 방식은 두 DataFrame에 모두 키 값이 존재하는 경우만 병합합니다.
- outer 방식은 하나의 DataFrame에 키 값이 존재하는 경우 모두 병합합니다.
- outer 방식에서는 없는 값은 NaN으로 대입됩니다.
df
df_right
pd.merge(df, df_right, on='이름', how='inner')
pd.merge(df, df_right, on='이름', how='outer')
df
df_right.columns = ['성함', '연봉', '가족수']
df_right
df와 df_right를 병합하려고 했더니, df에서는 '이름', df_right에서는 '성함'으로 표기되어 기준이 되는 column을 지정할 수 없습니다.
이럴 때는 left_on, right_on 옵션을 사용합니다.
pd.merge(df, df_right, left_on='이름', right_on='성함', how='outer')
df.info()
이전에 배운 info를 찍어보면, 우측에 type이 같이 찍히는 것을 확인하실 수 있습니다.
- object: 일반 문자열 타입
- float: 실수
- int: 정수
- category: 카테고리
- datetime: 시간
- type 변환을 위해서는 astype이라는 메소드를 사용합니다.
df['키'].dtypes
df['키'].astype(int)
에러가 발생했습니다..NaN 값이 들어있기 때문에 변경이 안됩니다.
이럴 때는 fillna로 빈값을 임의로 채워 주겠습니다.
df['키'] = df['키'].fillna(-1)
df['키'].astype(int)
int형으로 타입을 바꾸니 float -> int로 변경 된 것을 볼 수 있습니다.
- 날짜를 변환하기 위해서는 판다스 메소드인 to_datetime이라는 메소드를 사용합니다.
현재 날짜 column은 dtype:이 object, 즉 문자열 타입으로 되어 있습니다.
df['생년월일']
df['생년월일'] = pd.to_datetime(df['생년월일'])
datetime 타입으로 우리가 변환해준 중요한 이유가 있습니다!
- 매우 손쉽게, 월, 일, 요일 등등의 날짜 정보를 세부적으로 추출해낼 수 있습니다.
- datetime의 약어인 'dt'에는 다양한 정보들을 제공해 줍니다.
df['생년월일'].dt.year
df['생년월일'].dt.month
df['생년월일'].dt.day
df['생년월일'].dt.hour
df['생년월일'].dt.minute
df['생년월일'].dt.second
df['생년월일'].dt.dayofweek
월요일: 0 화요일: 1, 수요일: 2, 목요일: 3, 금요일: 4, 토요일: 5, 일요일: 6
df['생년월일'].dt.weekofyear
apply는 Series나 DataFrame에 좀 더 구체적인 로직을 적용하고 싶은 경우 활용합니다.
- apply를 적용하기 위해서는 함수가 먼저 정의되어야합니다.
- apply는 정의한 로직 함수를 인자로 넘겨줍니다.
df.loc[df['성별'] == '남자', '성별'] = 1
df.loc[df['성별'] == '여자', '성별'] = 0
df
- (목표) 남자/ 여자의 문자열 데이터로 구성된 '성별' column을 1 / 0 으로 바꿔보세요
df = pd.read_csv('http://bit.ly/ds-korean-idol')
[주의] 반드시 return 값이 존재하여야합니다.
남자: 1 여자: 0 기타: -1
def male_or_female(x):
if x == '남자':
return 1
elif x == '여자':
return 0
df['성별_NEW'] = df['성별'].apply(male_or_female)
df.head()
- (목표) cm당 브랜드 평판지수를 구해보세요 (브랜드평판지수/ 키)
def cm_to_brand(df):
value = df['브랜드평판지수'] / df['키']
return value
df.apply(cm_to_brand, axis=1)
- lambda는 1줄로 작성하는 간단 함수식입니다.
- return을 별도로 명기하지 않습니다
f = lambda x: 1 if x == '남자' else 0
df['성별'].apply(f)
실제로는 간단한 계산식을 적용하려는 경우에 많이 사용합니다.
df['키/2'] = df['키'].apply(lambda x: x / 2)
df.head()
df['키'].apply(lambda x: x ** 2)
apply에 함수식을 만들어서 적용해주는 것과 동일하기 때문에, 복잡한 조건식은 함수로 간단한 계산식은 lambda로 적용할 수 있습니다.
my_map = {
'남자': 1,
'여자': 0
}
df['성별'].map(my_map)
my_map = {
'남자': 'male',
'여자': 'female'
}
df['성별'].map(my_map)
df = pd.DataFrame({'통계': [60, 70, 80, 85, 75], '미술': [50, 55, 80, 100, 95], '체육': [70, 65, 50, 95, 100] })
type(df['통계'])
df['통계'] + df['미술'] + df['체육']
df['통계'] - df['미술']
df['통계'] * df['미술']
df['통계'] / df['미술']
df['통계'] % df['미술']
df['통계'] + 10
df = pd.DataFrame({'통계': [60, 70, 80, 85, 75], '미술': [50, 55, 80, 100, 95], '체육': [70, 65, 50, 95, 100] })
df['통계미술합계'] = df['통계'] + df['미술'] + 10
df
df = pd.DataFrame({'통계': [60, 70, 80, 85, 75], '미술': [50, 55, 80, 100, 95], '체육': [70, 65, 50, 95, 100] })
df.sum(axis=0) # == df.sum()
df.mean(axis=0) # == df.mean()
df.sum(axis=1)
df.mean(axis=1)
df = pd.DataFrame({'통계': [60, 70, np.nan , 85, 75], '미술': [50, np.nan , 80, 100, 95], '체육': [70, 65, 50, np.nan , 100] })
df['통계'] / 2
1000 / df['통계']
df['통계'] / np.nan
np.nan / df['통계']
df1 = pd.DataFrame({'통계': [60, 70, 80, 85, 75], '미술': [50, 55, 80, 100, 95], '체육': [70, 65, 50, 95, 100] })
df2 = pd.DataFrame({'통계': ['good', 'bad', 'ok' , 'good', 'ok'], '미술': [50, 60 , 80, 100, 95], '체육': [70, 65, 50, 70 , 100] })
문자열이 포함된 DataFrame의 경우
df1 + df2
column의 순서가 바뀌어 있는 경우
df1 = pd.DataFrame({'미술': [10, 20, 30, 40, 50], '통계':[60, 70, 80, 90, 100] })
df2 = pd.DataFrame({'통계': [10, 20, 30, 40, 50], '미술': [60, 70, 80, 90, 100] })
df1 + df2
행의 갯수가 다른경우
df1 = pd.DataFrame({'미술': [10, 20, 30, 40, 50, 60], '통계':[60, 70, 80, 90, 100, 110] })
df2 = pd.DataFrame({'통계': [10, 20, 30, 40, 50], '미술': [60, 70, 80, 90, 100] })
df1 * df2
df = pd.read_csv('https://bit.ly/ds-korean-idol')
df.info()
df.select_dtypes(include='object')
df.select_dtypes(exclude='object')
num_cols = df.select_dtypes(exclude='object').columns
obj_cols = df.select_dtypes(include='object').columns
df[obj_cols]
- 원핫인코딩은 한개의 요소는 True 그리고 나머지 요소는 False로 만들어 주는 기법입니다.
- 원핫인코딩은 왜 필요할까요?
blood_map = {
'A': 0,
'B': 1,
'AB': 2,
'O': 3,
}
df['혈액형_code'] = df['혈액형'].map(blood_map)
df.head()
df['혈액형_code'].value_counts()
- 우리가 만약 df['혈액형_code']를 머신러닝 알고리즘에 그대로 넣어 데이터를 예측하라고 지시한다면, 컴퓨터는 '혈액형_code'안에서 값들간의 관계를 스스로 형성하게 됩니다.
- 이 상황에서 만약 B형은 1, AB형은 2라는 값을 가지고 있는데, 컴퓨터는 B형 + AB형 = O형이다라고 잘못 관계를 맺을 수 있게 됩니다.
- 따라서, 우리는 4개의 별도의 column을 형성해주고 1개의 column에는 True 나머지는 모두 False를 넣어 줌으로써 A, B, AB, O형의 관계는 독립적이다를 표현해줍니다.
- 이를 원핫인코딩이라고 합니다.
df['혈액형_code']
pd.get_dummies(df['혈액형_code'])
prefix를 설정하려면?
pd.get_dummies(df['혈액형_code'], prefix='혈액형')
pd.get_dummies(df, columns = ['혈액형_code'])
import matplotlib.pyplot as plt
%matplotlib inline
import platform
if platform.system() == 'Darwin': # Mac 환경 폰트 설정
plt.rc('font', family='AppleGothic')
elif platform.system() == 'Windows': # Windows 환경 폰트 설정
plt.rc('font', family='Malgun Gothic')
plt.rc('axes', unicode_minus=False) # 마이너스 폰트 설정
%config InlineBackend.figure_format = 'retina'
df = pd.read_csv('https://bit.ly/ds-house-price-clean')
df.plot()
plt.rcParams["figure.figsize"] = (12, 9)
df.plot()
plot은 일반 선그래프를 나타냅니다.
kind 옵션을 통해 원하는 그래프를 그릴 수 있습니다.
kind 옵션:
- line: 선그래프
- bar: 바 그래프
- barh: 수평 바 그래프
- hist: 히스토그램
- kde: 커널 밀도 그래프
- hexbin: 고밀도 산점도 그래프
- box: 박스 플롯
- area: 면적 그래프
- pie: 파이 그래프
- scatter: 산점도 그래프
- line 그래프는 데이터가 연속적인 경우 사용하기 적절합니다. (예를 들면, 주가 데이터)
df['분양가'].plot(kind='line')
df_seoul = df.loc[df['지역'] == '서울']
df_seoul
df_seoul_year = df_seoul.groupby('연도').mean()
df_seoul_year
df_seoul_year['분양가'].plot(kind='line')
bar 그래프는 그룹별로 비교할 때 유용합니다.
df.groupby('지역')['분양가'].mean()
df.groupby('지역')['분양가'].mean().plot(kind='bar')
df.groupby('지역')['분양가'].mean().plot(kind='barh')
히스토그램은 분포-빈도 를 시각화하여 보여줍니다
가로축에는 분포를, 세로축에는 빈도가 시각화되어 보여집니다.
df['분양가'].plot(kind='hist')
- 히스토그램과 유사하게 밀도를 보여주는 그래프입니다.
- 히스토그램과 유사한 모양새를 갖추고 있습니다.
- 부드러운 라인을 가지고 있습니다.
df['분양가'].plot(kind='kde')
- hexbin은 고밀도 산점도 그래프입니다.
- x와 y 키 값을 넣어 주어야 합니다.
- x, y 값 모두 numeric 한 값을 넣어 주어야합니다.
- 데이터의 밀도를 추정합니다.
df.plot(kind='hexbin', x='분양가', y='연도', gridsize=20)
df_seoul = df.loc[df['지역'] == '서울']
df_seoul['분양가'].plot(kind='box')
df_seoul.describe()
area plot은 line 그래프에서 아래 area를 모두 색칠해 주는 것이 특징입니다.
df.groupby('월')['분양가'].count().plot(kind='line')
df.groupby('월')['분양가'].count().plot(kind='area')
pie는 대표적으로 데이터의 점유율을 보여줄 때 유용합니다.
df.groupby('연도')['분양가'].count().plot(kind='pie')
- 점으로 데이터를 표기해 줍니다
- x, y 값을 넣어주어야합니다 (hexbin과 유사)
- x축과 y축을 지정해주면 그에 맞는 데이터 분포도를 볼 수 있습니다.
- 역시 numeric 한 column 만 지정할 수 있습니다
df.plot(x='월', y='분양가', kind='scatter')