11_판다스7
import warnings
warnings.filterwarnings('ignore')
from IPython.display import Image
import numpy as np
import pandas as pd
import seaborn as sns
df = pd.read_csv('./data/gapminder.tsv', sep='\t')
df.head()
country | continent | year | lifeExp | pop | gdpPercap | |
---|---|---|---|---|---|---|
0 | Afghanistan | Asia | 1952 | 28.801 | 8425333 | 779.445314 |
1 | Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.853030 |
2 | Afghanistan | Asia | 1962 | 31.997 | 10267083 | 853.100710 |
3 | Afghanistan | Asia | 1967 | 34.020 | 11537966 | 836.197138 |
4 | Afghanistan | Asia | 1972 | 36.088 | 13079460 | 739.981106 |
#groupby()함수를 이용해서 특정 열을 기준으로 데이터를
#그룹화 할 수 있다.
age_lifeExp_by_year = df.groupby('year').lifeExp.mean()
age_lifeExp_by_year
# groupby() 함수와 같이 사용하는 집계 함수
# count() => 누락값을 제외한 데이터 개수
# size() => 누락값을 포함한 데이터 개수
# mean() => 평균
# std() => 표준편차
# min() => 최소값
# quantile(q = 0.25) => 1사분위수
# quantile(q = 0.50) => 중위수
# quantile(q = 0.75) => 3사분위수
# max() => 최대값
# sum() => 합계
# var() => 분산
# sem() => 평균의 표준편차
# describe() => 데이터 개수, 평균, 표준편차, 최소값, 사분위수, 최대값을 모두 반환
# first() => 첫 번째 행 반환
# last() => 마지막 행 반환
# nth() => n번재 행 반환
year
1952 49.057620
1957 51.507401
1962 53.609249
1967 55.678290
1972 57.647386
1977 59.570157
1982 61.533197
1987 63.212613
1992 64.160338
1997 65.014676
2002 65.694923
2007 67.007423
Name: lifeExp, dtype: float64
#agg() 함수를 사용하면 사용자 정의 함수를 groupby()함수로
#그룹화한 데이터에 일괄적으로 적용할 수 있다.
#agg()함수로 사용자 정의 함수와 groupby함수 조합하기.
#평균
def my_mean(values):
sum = 0
for i in values:
sum +=i
return (sum/len(values))
my_mean([1,2,3,4,5])
#판다스가 제공하는 집계함수는 groupby()함수로 그룹화한 결과가
#자동으로 전달되서 실행되지만 사용자 정의 함수에는 그렇게 안된다.
#agg() 함수의 인수로 실행할 사용자 정의 함수의 이름을 넘겨준다.
#=>이때 함수의 이름만 써야하고 ()는 입력하지 않는다.
#agg의 함수의 첫 번째 인수로 그룹화된 데이터가 자동으로 넘어간다.
age_my_mean = df.groupby('year').lifeExp.agg(my_mean)
age_my_mean
year
1952 49.057620
1957 51.507401
1962 53.609249
1967 55.678290
1972 57.647386
1977 59.570157
1982 61.533197
1987 63.212613
1992 64.160338
1997 65.014676
2002 65.694923
2007 67.007423
Name: lifeExp, dtype: float64
#year 별로 계산된 그룹 평균과 전체 평균의
#편차를 계산한다.
def my_mean_diff(values, diff_value):
n = len(values)
total = 0
for value in values:
total += value
mean = total / n
#그룹별 평균(mean)에서 전체 평균을 뺀
#값을 리턴시킨다
return mean - diff_value
#전체 평균을 계산한다.
global_mean = df.lifeExp.mean()
print(global_mean)
59.474439366197174
#그룹화된 첫번재 정보가 my_mean_diff의 첫번재 인수(values)로 전달되고
#diff_value란 두번째 인수엔 global_mean의 값을 넣어준다.
age_mean_diff = df.groupby('year').lifeExp.agg(my_mean_diff, diff_value=global_mean)
age_mean_diff
year
1952 -10.416820
1957 -7.967038
1962 -5.865190
1967 -3.796150
1972 -1.827053
1977 0.095718
1982 2.058758
1987 3.738173
1992 4.685899
1997 5.540237
2002 6.220483
2007 7.532983
Name: lifeExp, dtype: float64
#한개의 열에 대해서 여러개의 함수를 실행한다.
#그룹화한 한개의 열에 집계 함수를 2개 이상
#실행하려는 경우 agg()함수에 집계 함수를
#리스트에 담아서 전달한다.
#(이때 함수와 함께 ()를 넣지는 않는다.)
gdf = df.groupby('year').lifeExp.agg([np.count_nonzero, np.mean, np.std])
gdf
count_nonzero | mean | std | |
---|---|---|---|
year | |||
1952 | 142.0 | 49.057620 | 12.225956 |
1957 | 142.0 | 51.507401 | 12.231286 |
1962 | 142.0 | 53.609249 | 12.097245 |
1967 | 142.0 | 55.678290 | 11.718858 |
1972 | 142.0 | 57.647386 | 11.381953 |
1977 | 142.0 | 59.570157 | 11.227229 |
1982 | 142.0 | 61.533197 | 10.770618 |
1987 | 142.0 | 63.212613 | 10.556285 |
1992 | 142.0 | 64.160338 | 11.227380 |
1997 | 142.0 | 65.014676 | 11.559439 |
2002 | 142.0 | 65.694923 | 12.279823 |
2007 | 142.0 | 67.007423 | 12.073021 |
#여러개의 열에 대해서 여러개의 함수를 실행한다.
#그릅화한 2개 이상의 열에 집계 함수를 2개 이상
#사용하려는 경우 딕셔너리에 계산할 열과
#사용할 함수를 key와 value로 묶어서 전달하면
#된다.
gdf = df.groupby('year').agg({'lifeExp' : 'mean', 'pop' : 'median',
'gdpPercap' : 'max'})
gdf
lifeExp | pop | gdpPercap | |
---|---|---|---|
year | |||
1952 | 49.057620 | 3943953.0 | 108382.35290 |
1957 | 51.507401 | 4282942.0 | 113523.13290 |
1962 | 53.609249 | 4686039.5 | 95458.11176 |
1967 | 55.678290 | 5170175.5 | 80894.88326 |
1972 | 57.647386 | 5877996.5 | 109347.86700 |
1977 | 59.570157 | 6404036.5 | 59265.47714 |
1982 | 61.533197 | 7007320.0 | 33693.17525 |
1987 | 63.212613 | 7774861.5 | 31540.97480 |
1992 | 64.160338 | 8688686.5 | 34932.91959 |
1997 | 65.014676 | 9735063.5 | 41283.16433 |
2002 | 65.694923 | 10372918.5 | 44683.97525 |
2007 | 67.007423 | 10517531.0 | 49357.19017 |
#표준점수 계산하기
#데이터의 평균과 표준편차의 차이를
#표준점수라 부른다.
#표준점수를 구하면 변환된 데이터의
#평균이 0이 되고 표준편차는 1이
#된다. 그러면 데이터가 표준화되어
#서로 다른 데이터를 쉽게 비교할 수
#있다.
def my_zscore(x):
return(x-x.mean()) / x.std()
#agg() 함수는 그룹별 대표값을 만드는 함수이므로
#agg()함수로 실행하는 함수(my_zscore)에서는
#브로드캐스팅(x-x.mean())이 실행되면 안된다.
#dfz = df.groupby('year').lifeExp.agg(my_zscore)
#=>에러 발생
#브로드캐스팅을 실행해야 하는 함수를 실행해야
#한다면 transform()함수를 사용한다.
df['zscore'] = df.groupby('year').lifeExp.transform(my_zscore)
df
country | continent | year | lifeExp | pop | gdpPercap | zscore | |
---|---|---|---|---|---|---|---|
0 | Afghanistan | Asia | 1952 | 28.801 | 8425333 | 779.445314 | -1.656854 |
1 | Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.853030 | -1.731249 |
2 | Afghanistan | Asia | 1962 | 31.997 | 10267083 | 853.100710 | -1.786543 |
3 | Afghanistan | Asia | 1967 | 34.020 | 11537966 | 836.197138 | -1.848157 |
4 | Afghanistan | Asia | 1972 | 36.088 | 13079460 | 739.981106 | -1.894173 |
... | ... | ... | ... | ... | ... | ... | ... |
1699 | Zimbabwe | Africa | 1987 | 62.351 | 9216418 | 706.157306 | -0.081621 |
1700 | Zimbabwe | Africa | 1992 | 60.377 | 10704340 | 693.420786 | -0.336974 |
1701 | Zimbabwe | Africa | 1997 | 46.809 | 11404948 | 792.449960 | -1.574962 |
1702 | Zimbabwe | Africa | 2002 | 39.989 | 11926563 | 672.038623 | -2.093346 |
1703 | Zimbabwe | Africa | 2007 | 43.487 | 12311143 | 469.709298 | -1.948180 |
1704 rows × 7 columns
누락값을 평균값으로 처리하기
# seaborn 라이브러리의 tips데이터 집합에서
# 10개의 행 데이터만 가져온 다음
# total_bill열의 값4개를 임의로 선택해서
# 누락값으로 바꾼다.
np.random.seed(42)
# sample()함수는 인수로 지정된 개수만큼
# 전체 데이터에서 랜덤하게 데이터를 추출한다.
# import seaborn as sns 참조한다.
tips_10 = sns.load_dataset('tips').sample(10)
tips_10
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
24 | 19.82 | 3.18 | Male | No | Sat | Dinner | 2 |
6 | 8.77 | 2.00 | Male | No | Sun | Dinner | 2 |
153 | 24.55 | 2.00 | Male | No | Sun | Dinner | 4 |
211 | 25.89 | 5.16 | Male | Yes | Sat | Dinner | 4 |
198 | 13.00 | 2.00 | Female | Yes | Thur | Lunch | 2 |
176 | 17.89 | 2.00 | Male | Yes | Sun | Dinner | 2 |
192 | 28.44 | 2.56 | Male | Yes | Thur | Lunch | 2 |
124 | 12.48 | 2.52 | Female | No | Thur | Lunch | 2 |
9 | 14.78 | 3.23 | Male | No | Sun | Dinner | 2 |
101 | 15.38 | 3.00 | Female | Yes | Fri | Dinner | 2 |
#넘파이에서 permutation(), shuffle()함수는 무작위로 배열을 섞는다.
#차이점
#1. permutation() => 배열을 복사해서 리턴하기에 원본이 유지된다.
#2. shuffle() => 원본 자체를 수정한다.
x = np.arange(10)
print(x) #[0 1 2 3 4 5 6 7 8 9]이 출력된다. =>원본
#permutation == 순열
#섞인 결과가 출력된다.
print(np.random.permutation(x))
print(x) #[0 1 2 3 4 5 6 7 8 9]이 출력된다. => permutation()함수는 원본을 변형시키지 않는다.
print(np.random.shuffle(x)) #None이 출력된다. => 섞여서 리턴되는 값이 없다. =>원본 자체를 섞어버린다.
print(x) #원본 데이터가 섞여서 출력된다.
[0 1 2 3 4 5 6 7 8 9]
[5 3 4 2 9 1 6 7 0 8]
[0 1 2 3 4 5 6 7 8 9]
None
[0 3 7 4 2 9 5 8 6 1]
tips_10.loc[np.random.permutation(tips_10.index)[:4], 'total_bill'] = np.NaN
tips_10
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
24 | 19.82 | 3.18 | Male | No | Sat | Dinner | 2 |
6 | 8.77 | 2.00 | Male | No | Sun | Dinner | 2 |
153 | NaN | 2.00 | Male | No | Sun | Dinner | 4 |
211 | 25.89 | 5.16 | Male | Yes | Sat | Dinner | 4 |
198 | NaN | 2.00 | Female | Yes | Thur | Lunch | 2 |
176 | NaN | 2.00 | Male | Yes | Sun | Dinner | 2 |
192 | 28.44 | 2.56 | Male | Yes | Thur | Lunch | 2 |
124 | NaN | 2.52 | Female | No | Thur | Lunch | 2 |
9 | 14.78 | 3.23 | Male | No | Sun | Dinner | 2 |
101 | 15.38 | 3.00 | Female | Yes | Fri | Dinner | 2 |
#total_bill열의 누락값을 단순히 total_bill열의 전체
#평균으로 채우면 안된다.
#현재 tips_10의 데이터는 여성보다 남성이 많기 때문에
#여성과 넘성을 구분해서 total_bill열의 평균값을
#계산하지 않으면 여성 데이터가 남성 데이터의 영향(간섭)을
#받아서 여성 데이터가 훼손될 수 있다.
#성별로 그룹화 해 보면 남성의 누락값은 3개 여성의 누락값은
#한개인 것을 확인할 수 있다.
count_sex = tips_10.groupby('sex').count()
count_sex
total_bill | tip | smoker | day | time | size | |
---|---|---|---|---|---|---|
sex | ||||||
Male | 5 | 7 | 7 | 7 | 7 | 7 |
Female | 1 | 3 | 3 | 3 | 3 | 3 |
#성별을 구분해서 total_bill열의 데이터를 받아
#평균을 계산하는 함수
def fill_na_mean(x):
avg = x.mean()
return x.fillna(avg)
total_bill_group_mean = tips_10.groupby('sex').total_bill.transform(fill_na_mean)
total_bill_group_mean
24 19.82
6 8.77
153 19.54
211 25.89
198 15.38
176 19.54
192 28.44
124 15.38
9 14.78
101 15.38
Name: total_bill, dtype: float64
tips_10.total_bill.mean()
18.846666666666668
tips_10.groupby('sex').total_bill.mean()
sex
Male 19.54
Female 15.38
Name: total_bill, dtype: float64
누락값을 남,여 구분없에 전체 평균으로 체운다.
np.random.seed(42)
tips_10 = sns.load_dataset('tips').sample(10)
tips_10.loc[np.random.permutation(tips_10.index)[:4], 'total_bill'] = np.NaN
tips_10
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
24 | 19.82 | 3.18 | Male | No | Sat | Dinner | 2 |
6 | 8.77 | 2.00 | Male | No | Sun | Dinner | 2 |
153 | NaN | 2.00 | Male | No | Sun | Dinner | 4 |
211 | NaN | 5.16 | Male | Yes | Sat | Dinner | 4 |
198 | NaN | 2.00 | Female | Yes | Thur | Lunch | 2 |
176 | NaN | 2.00 | Male | Yes | Sun | Dinner | 2 |
192 | 28.44 | 2.56 | Male | Yes | Thur | Lunch | 2 |
124 | 12.48 | 2.52 | Female | No | Thur | Lunch | 2 |
9 | 14.78 | 3.23 | Male | No | Sun | Dinner | 2 |
101 | 15.38 | 3.00 | Female | Yes | Fri | Dinner | 2 |
tips_10['total_bill'] = tips_10.total_bill.fillna(tips_10.total_bill.mean())
tips_10
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
24 | 19.820000 | 3.18 | Male | No | Sat | Dinner | 2 |
6 | 8.770000 | 2.00 | Male | No | Sun | Dinner | 2 |
153 | 16.611667 | 2.00 | Male | No | Sun | Dinner | 4 |
211 | 16.611667 | 5.16 | Male | Yes | Sat | Dinner | 4 |
198 | 16.611667 | 2.00 | Female | Yes | Thur | Lunch | 2 |
176 | 16.611667 | 2.00 | Male | Yes | Sun | Dinner | 2 |
192 | 28.440000 | 2.56 | Male | Yes | Thur | Lunch | 2 |
124 | 12.480000 | 2.52 | Female | No | Thur | Lunch | 2 |
9 | 14.780000 | 3.23 | Male | No | Sun | Dinner | 2 |
101 | 15.380000 | 3.00 | Female | Yes | Fri | Dinner | 2 |
댓글남기기