24 분 소요

filter()

  • DataFrameGroupBy.filter(func, dropna=True, *args, **kwargs)
  • 특정 조건을 만족하는 Group의 데이터(행)들을 조회할 때 사용한다. 주로 조건은 group별 집계결과를 이용한다.
    1. 함수에 group별 DataFrame을 argument로 전달한다.
    2. 함수는 받은 DataFrame을 이용해 집계한 값의 조건을 비교해서 반환한다.(반환타입: Bool)
    3. 반환값이 True인 Group들의 모든 행들로 구성된 DataFrame을 반환한다.
  • 매개변수
    • func: filtering 조건을 구현한 함수 객체
      • 첫번째 매개변수로 Group으로 묶인 DataFrame을 받는다.
      • bool type 값을 반환한다. 매개변수로 받은 DataFrame이 특정 조건을 만족하는지 여부를 반환한다.
    • dropna=True
      • 필터를 통과하지 못한 group의 DataFrame의 값들을 drop시킨다(기본값). False로 설정하면 NA 처리해서 반환한다.
    • *args, **kwargs: filter 함수의 두번째부터 선언된 매개변수에 전달할 argument 값들을 가변인자로 전달한다.
import numpy as np
import pandas as pd

# cnt1 값의 범위: 사과: 10대, 귤: 20대, 배: 단단위, 딸기 30이상
data = dict(fruits=['사과', '사과','사과', '사과','사과','귤','귤','귤','귤','귤','배','배','배','배','배','딸기','딸기','딸기','딸기','딸기']
            ,cnt1=[10, 12, 13, 11, 12, 21, 22, 27, 24, 26, 7, 7, 8, 3, 2, 30, 35, 37, 41, 28]
            ,cnt2=[100,  103, 107, 107,  101,  51,  57, 58,  57, 51,  9, 9,  5,  7,  7,  208, 217, 213, 206, 204]
           )
df = pd.DataFrame(data)
df
fruits cnt1 cnt2
0 사과 10 100
1 사과 12 103
2 사과 13 107
3 사과 11 107
4 사과 12 101
5 21 51
6 22 57
7 27 58
8 24 57
9 26 51
10 7 9
11 7 9
12 8 5
13 3 7
14 2 7
15 딸기 30 208
16 딸기 35 217
17 딸기 37 213
18 딸기 41 206
19 딸기 28 204
  • filter 함수 - cnt1의 평균이 20 이상인 과일 데이터들을 조회한다.
result = df.groupby('fruits')['cnt1'].mean()
result2= result[result >= 20]
result.index
df[df['fruits'].isin(result2.index)]
fruits cnt1 cnt2
5 21 51
6 22 57
7 27 58
8 24 57
9 26 51
15 딸기 30 208
16 딸기 35 217
17 딸기 37 213
18 딸기 41 206
19 딸기 28 204
def check_mean(df):
    """
    df - DataFrame. filter 에 사용하면 Group별 DataFrame이 넘어온다.
    """
    return df['cnt1'].mean() >= 20
df.groupby('fruits').filter(check_mean)
fruits cnt1 cnt2
5 21 51
6 22 57
7 27 58
8 24 57
9 26 51
15 딸기 30 208
16 딸기 35 217
17 딸기 37 213
18 딸기 41 206
19 딸기 28 204
  • lambda 표현식 이용
df.groupby('fruits').filter(lambda df : df['cnt1'].mean() > 20) 
#return 결과가 True인 group의 dataframe이 결과에 추가.
fruits cnt1 cnt2
5 21 51
6 22 57
7 27 58
8 24 57
9 26 51
15 딸기 30 208
16 딸기 35 217
17 딸기 37 213
18 딸기 41 206
19 딸기 28 204
  • Parameter가 있는 filter 처리함수.
  • 컬럼명, threshold값을 parameter로 받는다.
def check_mean2(df:pd.DataFrame, column_name:str, threshold:int):
    return df[column_name].mean() >= threshold
  • 1번 parameter - group 별 DataFrame이 전달. 2번째 parameter 부터는 넘겨줘야한다.
df.groupby('fruits').filter(check_mean2, column_name='cnt1', threshold= 30)
df.groupby('fruits').filter(check_mean2, column_name='cnt2', threshold= 100)
df.groupby('fruits').filter(check_mean2, dropna=False, column_name='cnt2', threshold= 100)
# 3번째는 잘 사용은 안하나 이렇게도 할 수 있다!
fruits cnt1 cnt2
0 사과 10.0 100.0
1 사과 12.0 103.0
2 사과 13.0 107.0
3 사과 11.0 107.0
4 사과 12.0 101.0
5 NaN NaN NaN
6 NaN NaN NaN
7 NaN NaN NaN
8 NaN NaN NaN
9 NaN NaN NaN
10 NaN NaN NaN
11 NaN NaN NaN
12 NaN NaN NaN
13 NaN NaN NaN
14 NaN NaN NaN
15 딸기 30.0 208.0
16 딸기 35.0 217.0
17 딸기 37.0 213.0
18 딸기 41.0 206.0
19 딸기 28.0 204.0

transform

  • DataFrameGroupBy.transform(func, *args), SeriesGroupBy.transform(func, *args)
    • 함수(func)에 열의 값들을 group 별로 전달 한다. 함수는 그 값을 받아 통계량을 구해 반환한다. 반환된 통계량으로 원래 값들을 변경한 Series를 반환한다. 여러 컬럼에 대해 처리할 경우 DataFrame을 반환한다.
    • func: 함수객체
      • 매개변수
        • 그룹별 컬럼값들을 받을 변수 선언
      • return
        • 계산한 통계량.
      • DataFrameGroupBy은 모든 컬럼의 값들을 group 별 Series로 전달한다.
    • *args: 함수에 전달할 추가 인자값이 있으면 매개변수 순서에 맞게 값을 전달한다.
  • transform() 함수를 groupby() 와 사용하면 컬럼의 각 원소들을 자신이 속한 그룹의 통계량으로 변환된 데이터셋을 생성할 수 있다.
  • 컬럼의 값과 통계값을 비교해서 보거나 결측치 처리등에 사용할 수있다.
  • 디테일하게 그룹별로 비교할때 사용한다.

원본에 통계치 붙여서 비교하기

  • DataFrameGroupby -> group별로 각 컬럼의 값을 함수에 전달.
  • 사과->cnt1, 귤->cnt1, 사과-> cnt2, 귤->cnt2
df.groupby('fruits').transform('mean')
cnt1 cnt2 cnt2 cnt2
0 11.6 103.6
1 11.6 103.6
2 11.6 103.6
3 11.6 103.6
4 11.6 103.6
5 24.0 54.8
6 24.0 54.8
7 24.0 54.8
8 24.0 54.8
9 24.0 54.8
10 5.4 7.4
11 5.4 7.4
12 5.4 7.4
13 5.4 7.4
14 5.4 7.4
15 34.2 209.6
16 34.2 209.6
17 34.2 209.6
18 34.2 209.6
19 34.2 209.6
df.groupby('fruits')['cnt1'].transform('mean')
0     11.6
1     11.6
2     11.6
3     11.6
4     11.6
5     24.0
6     24.0
7     24.0
8     24.0
9     24.0
10     5.4
11     5.4
12     5.4
13     5.4
14     5.4
15    34.2
16    34.2
17    34.2
18    34.2
19    34.2
Name: cnt1, dtype: float64
result = df.groupby('fruits').transform('mean')
result
cnt1 cnt2
0 11.6 103.6
1 11.6 103.6
2 11.6 103.6
3 11.6 103.6
4 11.6 103.6
5 24.0 54.8
6 24.0 54.8
7 24.0 54.8
8 24.0 54.8
9 24.0 54.8
10 5.4 7.4
11 5.4 7.4
12 5.4 7.4
13 5.4 7.4
14 5.4 7.4
15 34.2 209.6
16 34.2 209.6
17 34.2 209.6
18 34.2 209.6
19 34.2 209.6
  • 컬럼(열) 삽입 - df.insert(삽입할 컬럼 순번, 컬럼이름, 값들)
df.insert(1, "cnt1평균", result['cnt1'])
df
df['cnt2평균'] = result['cnt2']
df
fruits cnt1평균 cnt1 cnt2 cnt2평균
0 사과 11.6 10 100 103.6
1 사과 11.6 12 103 103.6
2 사과 11.6 13 107 103.6
3 사과 11.6 11 107 103.6
4 사과 11.6 12 101 103.6
5 24.0 21 51 54.8
6 24.0 22 57 54.8
7 24.0 27 58 54.8
8 24.0 24 57 54.8
9 24.0 26 51 54.8
10 5.4 7 9 7.4
11 5.4 7 9 7.4
12 5.4 8 5 7.4
13 5.4 3 7 7.4
14 5.4 2 7 7.4
15 딸기 34.2 30 208 209.6
16 딸기 34.2 35 217 209.6
17 딸기 34.2 37 213 209.6
18 딸기 34.2 41 206 209.6
19 딸기 34.2 28 204 209.6
df2 = pd.DataFrame(data)
df2.sample(n=3) # random하게 행을 추출해준다.
df2.sample(frac=0.3) # 비율 (전체중 30%의 행만 추출)
fruits cnt1 cnt2
12 8 5
2 사과 13 107
18 딸기 41 206
14 2 7
8 24 57
10 7 9
df2 = df2.sample(frac=1) # frac= 1.0 -> 섞기만 하겠다.
df2.reset_index(drop=True)
fruits cnt1 cnt2
0 24 57
1 사과 13 107
2 딸기 37 213
3 딸기 41 206
4 딸기 35 217
5 사과 12 101
6 딸기 28 204
7 사과 11 107
8 2 7
9 7 9
10 27 58
11 3 7
12 딸기 30 208
13 사과 10 100
14 21 51
15 사과 12 103
16 26 51
17 8 5
18 22 57
19 7 9
cnt1_avg = df2.groupby('fruits')['cnt1'].transform('mean')
df2.insert(3, '과일별 cnt1평균', cnt1_avg)
df2
fruits cnt1 cnt2 과일별 cnt1평균
8 24 57 24.0
2 사과 13 107 11.6
17 딸기 37 213 34.2
18 딸기 41 206 34.2
16 딸기 35 217 34.2
4 사과 12 101 11.6
19 딸기 28 204 34.2
3 사과 11 107 11.6
14 2 7 5.4
10 7 9 5.4
7 27 58 24.0
13 3 7 5.4
15 딸기 30 208 34.2
0 사과 10 100 11.6
5 21 51 24.0
1 사과 12 103 11.6
9 26 51 24.0
12 8 5 5.4
6 22 57 24.0
11 7 9 5.4

결측치 처리

  • transform이용해서 결측치를 같은 과일별 평균값으로 변환
    • 전체 평균보다 좀더 정확할 수 있다.
import numpy as np
s = pd.Series([1, np.nan, 2, np.nan])
s
0    1.0
1    NaN
2    2.0
3    NaN
dtype: float64
  • 결측치(nan)을 다른값을 대체(변경)
s.fillna(20) # 모든 결측치값을 20으로 변경
0     1.0
1    20.0
2     2.0
3    20.0
dtype: float64
  • s.fillna(s와 동일한 크기의 Series) -> 결측치는 결측치와 같은 index의 값으로 변경. 결측치 아닌 것들은 유지.
s.fillna(pd.Series([100,200,300,400]))
0      1.0
1    200.0
2      2.0
3    400.0
dtype: float64
df3 = pd.DataFrame(data)
df3.loc[[0, 1, 5, 6, 10, 11, 15, 16], 'cnt2'] = np.nan
df3
fruits cnt1 cnt2
0 사과 10 NaN
1 사과 12 NaN
2 사과 13 107.0
3 사과 11 107.0
4 사과 12 101.0
5 21 NaN
6 22 NaN
7 27 58.0
8 24 57.0
9 26 51.0
10 7 NaN
11 7 NaN
12 8 5.0
13 3 7.0
14 2 7.0
15 딸기 30 NaN
16 딸기 35 NaN
17 딸기 37 213.0
18 딸기 41 206.0
19 딸기 28 204.0
  • DataFrame 에서 결측치를 제거 -> 행 제거. axis=1 하면 컬럼(열)을 제거.
df3.dropna()
fruits cnt1 cnt2
2 사과 13 107.0
3 사과 11 107.0
4 사과 12 101.0
7 27 58.0
8 24 57.0
9 26 51.0
12 8 5.0
13 3 7.0
14 2 7.0
17 딸기 37 213.0
18 딸기 41 206.0
19 딸기 28 204.0
cnt2_mean = df3['cnt2'].mean()
cnt2_mean
93.58333333333333
df3['cnt2'] = df3['cnt2'].fillna(round(cnt2_mean)) #결측치가 채워짐
df3
fruits cnt1 cnt2
0 사과 10 94.0
1 사과 12 94.0
2 사과 13 107.0
3 사과 11 107.0
4 사과 12 101.0
5 21 94.0
6 22 94.0
7 27 58.0
8 24 57.0
9 26 51.0
10 7 94.0
11 7 94.0
12 8 5.0
13 3 7.0
14 2 7.0
15 딸기 30 94.0
16 딸기 35 94.0
17 딸기 37 213.0
18 딸기 41 206.0
19 딸기 28 204.0
df3['cnt2'] = df3['cnt2'].fillna(round(df3.groupby('fruits')['cnt2'].transform('mean')))
df3
fruits cnt1 cnt2
0 사과 10 105.0
1 사과 12 105.0
2 사과 13 107.0
3 사과 11 107.0
4 사과 12 101.0
5 21 55.0
6 22 55.0
7 27 58.0
8 24 57.0
9 26 51.0
10 7 6.0
11 7 6.0
12 8 5.0
13 3 7.0
14 2 7.0
15 딸기 30 208.0
16 딸기 35 208.0
17 딸기 37 213.0
18 딸기 41 206.0
19 딸기 28 204.0

TODO

1. data/diamonds.csv 조회

dia = pd.read_csv('data/diamonds.csv')
dia
carat cut color clarity depth table price x y z
0 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
1 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
2 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
3 0.29 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63
4 0.31 Good J SI2 63.3 58.0 335 4.34 4.35 2.75
... ... ... ... ... ... ... ... ... ... ...
53935 0.72 Ideal D SI1 60.8 57.0 2757 5.75 5.76 3.50
53936 0.72 Good D SI1 63.1 55.0 2757 5.69 5.75 3.61
53937 0.70 Very Good D SI1 62.8 60.0 2757 5.66 5.68 3.56
53938 0.86 Premium H SI2 61.0 58.0 2757 6.15 6.12 3.74
53939 0.75 Ideal D SI2 62.2 55.0 2757 5.83 5.87 3.64

53940 rows × 10 columns

2. cut 별 평균 가격이 4000 이상인 diamond 데이터들 조회

  • cut 별 평균가격.
dia1 = dia.groupby('cut')['price'].mean()
dia1[dia1 >= 4000]
cut
Fair       4358.757764
Premium    4584.257704
Name: price, dtype: float64
result = dia.groupby('cut').filter(lambda x : x['price'].mean() >= 4000)
result['cut'].value_counts()
Premium    13791
Fair        1610
Name: cut, dtype: int64
def check_mean(x):
    return x['price'].mean()>=4000
dia.groupby('cut').filter(check_mean)['cut'].value_counts()
Premium    13791
Fair        1610
Name: cut, dtype: int64

3. color 별 carat의 최대값과 최소값의 차이가 2이상 3미만인 모든 diamond 데이터들 조회

def dia_data(x):
    diff = x['carat'].max() - x['carat'].min()
    return diff >= 2 and diff < 3
result4 = dia.groupby('color').filter(dia_data)
result4
carat cut color clarity depth table price x y z
0 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
1 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
2 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
8 0.22 Fair E VS2 65.1 61.0 337 3.87 3.78 2.49
12 0.22 Premium F SI1 60.4 61.0 342 3.88 3.84 2.33
... ... ... ... ... ... ... ... ... ... ...
53929 0.71 Ideal G VS1 61.4 56.0 2756 5.76 5.73 3.53
53930 0.71 Premium E SI1 60.5 55.0 2756 5.79 5.74 3.49
53931 0.71 Premium F SI1 59.8 62.0 2756 5.74 5.73 3.43
53932 0.70 Very Good E VS2 60.5 59.0 2757 5.71 5.76 3.47
53933 0.70 Very Good E VS2 61.2 59.0 2757 5.69 5.72 3.49

30631 rows × 10 columns

dia.groupby('color')['carat'].agg(lambda x : x.max() - x.min()) # 결과 확인
color
D    3.20
E    2.85
F    2.81
G    2.78
H    3.90
I    3.78
J    4.78
Name: carat, dtype: float64

4. clarity 별 평균 가격 컬럼을 DataFrame에 추가.

result2 = dia.groupby('clarity')['price'].transform('mean')
dia2.insert(2,'clarity 평균가격임', round(result2))
dia2.head()
carat cut clarity 평균가격임 color clarity depth table price x y z
0 0.23 Ideal 5063.0 E SI2 61.5 55.0 326 3.95 3.98 2.43
1 0.21 Premium 3996.0 E SI1 59.8 61.0 326 3.89 3.84 2.31
2 0.23 Good 3839.0 E VS1 56.9 65.0 327 4.05 4.07 2.31
3 0.29 Premium 3925.0 I VS2 62.4 58.0 334 4.20 4.23 2.63
4 0.31 Good 5063.0 J SI2 63.3 58.0 335 4.34 4.35 2.75

pivot_table()

엑셀의 pivot table 기능을 제공하는 메소드.
분류별 집계(Group으로 묶어 집계)를 처리하는 함수로 group으로 묶고자 하는 컬럼들을 행과 열로 위치시키고 집계값을 값으로 보여준다.
역할은 groupby()를 이용한 집계와 같은데 여러개 컬럼을 기준으로 groupby 를 할 경우 집계결과를 읽는 것이 더 편하다.(가독성이 좋다)

pivot() 함수와 역할이 다르다.
pivot() 은 index와 column의 형태를 바꾸는 reshape 함수.

  • DataFrame.pivot_table(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
  • 매개변수
    • index
      • 문자열 또는 리스트. index로 올 컬럼들 => groupby였으면 묶었을 컬럼
    • columns
      • 문자열 또는 리스트. column으로 올 컬럼들 => groupby였으면 묶었을 컬럼 (index/columns가 묶여서 groupby에 묶을 컬럼들이 된다.)
    • values
      • 문자열 또는 리스트. 집계할 대상 컬럼들
    • aggfunc
      • 집계함수 지정. 함수, 함수이름문자열, 함수리스트(함수이름 문자열/함수객체), dict: 집계할 함수
      • 기본(생략시): 평균을 구한다. (mean이 기본값)
    • fill_value, dropna
      • fill_value: 집계시 NA가 나올경우 채울 값
      • dropna: boolean. 컬럼의 전체값이 NA인 경우 그 컬럼 제거(기본: True)
    • margins/margins_name
      • margin: boolean(기본: False). 총집계결과를 만들지 여부.
      • margin_name: margin의 이름 문자열로 지정 (생략시 All)
flights = pd.read_csv('data/flights.csv')
flights.groupby(['ORG_AIR','AIRLINE'])['DEP_DELAY'].mean()
ORG_AIR  AIRLINE
ATL      AA          7.995652
         AS         15.307692
         DL          7.616505
         EV         10.140896
         F9         21.574324
                      ...    
SFO      OO         11.135827
         UA         13.858655
         US          5.470588
         VX          8.144654
         WN         11.185714
Name: DEP_DELAY, Length: 114, dtype: float64

1개의 컬럼을 grouping 해서 집계

  • 항공사별 비행시간의 평균
  • 사용컬럼
    • grouping할 컬럼
      • AIRLINE: 항공사
    • 집계대상컬럼
      • AIR_TIME
  • 집계: mean
# flights.groupby('AIRLINE')['AIR_TIME'].mean()
flights.pivot_table(index="AIRLINE",values="AIR_TIME",aggfunc = "mean") # 반환 :DataFrame
flights.pivot_table(columns="AIRLINE",values="AIR_TIME") # aggfunc의 기본은: 평균!!!!
flights.pivot_table(index="AIRLINE", values="AIR_TIME", aggfunc = "mean", margins=True, margins_name='총계')

# 사용할 컬럼이 2개 이상일때만 피벗테이블 사용!!!
AIR_TIME
AIRLINE
AA 144.259404
AS 147.845052
B6 209.412963
DL 115.334187
EV 68.964016
F9 127.592337
HA 338.288288
MQ 61.318346
NK 135.736878
OO 76.010272
UA 155.650521
US 147.686755
VX 154.864097
WN 107.005897
총계 115.928576
flights.groupby('AIRLINE')['AIR_TIME'].mean().to_frame() # Series -> Dataframe!!!! 'to_frame()'
AIR_TIME
AIRLINE
AA 144.259404
AS 147.845052
B6 209.412963
DL 115.334187
EV 68.964016
F9 127.592337
HA 338.288288
MQ 61.318346
NK 135.736878
OO 76.010272
UA 155.650521
US 147.686755
VX 154.864097
WN 107.005897

두개의 컬럼을 grouping 해서 집계

  • 항공사/출발공항코드 별 취소 총수 (1이 취소이므로 합계를 구한다.)
  • 사용컬럼
    • grouping할 컬럼
      • AIRLINE: 항공사
      • ORG_AIR: 출발 공항코드
    • 집계대상컬럼
      • CANCELLED: 취소여부 - 1:취소, 0: 취소안됨
  • 집계: sum
flights.groupby(['AIRLINE', "ORG_AIR"])['CANCELLED'].sum()
AIRLINE  ORG_AIR
AA       ATL         3
         DEN         4
         DFW        86
         IAH         3
         LAS         3
                    ..
WN       LAS         7
         LAX        32
         MSP         1
         PHX         6
         SFO        25
Name: CANCELLED, Length: 114, dtype: int64
flights.pivot_table(index="AIRLINE", columns="ORG_AIR", values="CANCELLED",aggfunc="sum",
                   fill_value=-9999, #결측치를 대체할 값. (결측치 - 그 그룹으로 묶인 행이 없음.)
                   margins= True)
ORG_AIR ATL DEN DFW IAH LAS LAX MSP ORD PHX SFO All
AIRLINE
AA 3 4 86 3 3 11 3 35 4 2 154
AS 0 0 0 0 0 0 0 0 0 0 0
B6 -9999 0 0 -9999 0 0 -9999 0 0 1 1
DL 28 1 0 0 1 1 4 0 1 2 38
EV 18 6 27 36 -9999 -9999 6 53 0 -9999 146
F9 0 2 1 0 1 1 1 4 0 0 10
HA -9999 -9999 -9999 -9999 0 0 -9999 -9999 0 0 0
MQ 5 -9999 62 0 -9999 0 0 85 -9999 -9999 152
NK 1 1 6 0 1 1 3 10 2 -9999 25
OO 3 25 2 10 0 15 4 41 9 33 142
UA 2 9 1 23 3 6 2 25 3 19 93
US 0 0 2 2 1 0 0 6 7 3 21
VX -9999 -9999 -9999 -9999 0 3 -9999 0 -9999 3 6
WN 9 13 -9999 -9999 7 32 1 -9999 6 25 93
All 69 61 187 74 17 70 24 259 32 88 881

3개 이상의 컬럼을 grouping해서 집계

  • 항공사/월/출발공항코드 별 취소 총수
  • grouping할 컬럼
    • AIRLINE:항공사
    • MONTH:월
    • ORG_AIR: 출발지 공항
  • 집계 대상컬럼
    • CANCELLED: 취소여부
  • 집계 : sum
flights.groupby(['AIRLINE',"MONTH","ORG_AIR"])['CANCELLED'].sum()
AIRLINE  MONTH  ORG_AIR
AA       1      ATL        0
                DEN        0
                DFW        8
                IAH        0
                LAS        0
                          ..
WN       12     LAS        1
                LAX        2
                MSP        0
                PHX        0
                SFO        0
Name: CANCELLED, Length: 1133, dtype: int64
flights.pivot_table(index=['AIRLINE', "ORG_AIR"],columns="MONTH", values = "CANCELLED", aggfunc='sum')
MONTH 1 2 3 4 5 6 7 8 9 11 12
AIRLINE ORG_AIR
AA ATL 0.0 2.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
DEN 0.0 1.0 0.0 0.0 2.0 0.0 0.0 0.0 0.0 0.0 1.0
DFW 8.0 33.0 13.0 4.0 8.0 7.0 1.0 2.0 1.0 3.0 6.0
IAH 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0
LAS 0.0 2.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ...
WN LAS 1.0 1.0 1.0 0.0 0.0 0.0 1.0 1.0 0.0 1.0 1.0
LAX 3.0 2.0 3.0 2.0 1.0 0.0 9.0 4.0 3.0 3.0 2.0
MSP 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
PHX 0.0 2.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0
SFO 4.0 5.0 0.0 2.0 2.0 6.0 0.0 2.0 2.0 2.0 0.0

114 rows × 11 columns

3개 이상의 컬럼을 grouping해서 집계 2

  • 항공사/월/출발공항코드 별 최대/최소 연착시간
  • grouping할 컬럼
    • AIRLINE:항공사
    • MONTH:월
    • ORG_AIR: 출발지 공항
  • 집계 대상컬럼
    • ARR_DELAY: 연착시간
  • 집계 : min, max
flights.groupby(["AIRLINE","MONTH","ORG_AIR"])['ARR_DELAY'].agg(['min','max'])
min max
AIRLINE MONTH ORG_AIR
AA 1 ATL -27.0 26.0
DEN -13.0 78.0
DFW -39.0 287.0
IAH -23.0 63.0
LAS -32.0 732.0
... ... ... ... ...
WN 12 LAS -52.0 96.0
LAX -30.0 493.0
MSP -23.0 90.0
PHX -30.0 254.0
SFO -20.0 215.0

1133 rows × 2 columns

result = flights.pivot_table(index='MONTH',columns=['AIRLINE','ORG_AIR'], values='ARR_DELAY', aggfunc=['min','max'])
result.to_excel('saved_data/result.xlsx')

apply() - Series, DataFrame의 데이터 일괄 처리

데이터프레임의 행들과 열들 또는 Series의 원소들에 공통된 처리를 할 때 apply 함수를 이용하면 반복문을 사용하지 않고 일괄 처리가 가능하다.

  • DataFrame.apply(함수, axis=0, args=(), **kwarg)
    • 인수로 행이나 열을 받는 함수를 apply 메서드의 인수로 넣으면 데이터프레임의 행이나 열들을 하나씩 함수에 전달한다.
    • 매개변수
      • 함수: DataFrame의 행들 또는 열들을 전달할 함수
      • axis: 0-컬럼(열)을 전달, 1-행을 전달 (기본값 0)
      • args: 함수에 행/열 이외에 전달할 매개변수를 위치기반(순서대로) 튜플로 전달
      • **kwarg: 함수에 행/열 이외에 전달할 매개변수를 키워드 인자로 전달
  • Series.apply(함수, args=(), **kwarg)
    • 인수로 Series의 원소들을 받는 함수를 apply 메소드의 인수로 넣으면 Series의 원소들을 하나씩 함수로 전달한다.
    • 매개변수
      • 함수: Series의 원소들을 전달할 함수
      • args: 함수에 원소 이외에 전달할 매개변수를 위치기반(순서대로) 튜플로 전달
      • **kwarg: 함수에 원소 이외에 전달할 매개변수를 키워드 인자로 전달
import numpy as np
import pandas as pd

a = np.arange(24).reshape(6,4) # agrange(24) 0 ~ 23 1씩 증가하는 정수로 구성된 1차원 배열
# reshape(6,4) 배열의 형태(shape)을 변경 - 6 x 4 2차원 배열로 변환.
df = pd.DataFrame(a, columns=['no1', 'no2', 'no3', 'no4'])
df
no1 no2 no3 no4
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
4 16 17 18 19
5 20 21 22 23
def func1(x):
    """
    x : DataFrame.apply() - Series가 전달(열-axis=0/행-axis=1)
        Series.apply() - Series 를 구성하는 원소 하나.
    """
#     print(x.name)
    if x.name in ['no1','no3']:
        return x*100
    else:
        return x/100

df.apply(func1)
no1 no2 no3 no4
0 0 0.01 200 0.03
1 400 0.05 600 0.07
2 800 0.09 1000 0.11
3 1200 0.13 1400 0.15
4 1600 0.17 1800 0.19
5 2000 0.21 2200 0.23
df['no1'].name # Series를 조회한 컬럼/index 이름
'no1'
df.iloc[3].name
df.apply(lambda x : x.max()-x.min()) # 
no1    20
no2    20
no3    20
no4    20
dtype: int64
def func2(x):
#     print(x)
#     print('---------------')
    return x * 10
df['no1'].apply(func2) # Series
0      0
1     40
2     80
3    120
4    160
5    200
Name: no1, dtype: int64
df.apply(lambda x : x.mean() - x.median())
no1    0.0
no2    0.0
no3    0.0
no4    0.0
dtype: float64

cut()/qcut() - 연속형(실수)을 범주형으로 변환

  • cut() : 지정한 값을 기준으로 구간을 나눠 그룹으로 묶는다.
    • pd.cut(x, bins,right=True, labels=None)
    • 매개변수
      • x: 범주형으로 바꿀 대상. 1차원 배열형태(Series, 리스트, ndarray)의 자료구조
      • bins: 범주로 나눌때의 기준값(구간경계)들을 리스트로 묶어서 전달한다.
      • right: 구간경계의 오른쪽(True-기본)을 포함할지 왼쪽(False)을 포함할지
      • labels: 각 구간(범주)의 label을 리스트로 전달
        • 생략하면 범위를 범주명으로 사용한다. (ex: (10, 20], ()-포함안함, []-포함)
  • qcut() : 대상배열의 최대값 ~ 최소값을 지정한 개수의 동등한 size(원소의개수)가 되도록 나눈다.
    • pd.qcut(x, q, labels)
    • 매개변수
      • x: 나눌 대상. 1차원 배열형태의 자료구조
      • q: 나눌 개수
      • labels: 각 구간(범주)의 label을 리스트로 전달
import numpy as np
np.random.seed(0)
age = np.random.randint(1, 100, size=30) # 1 ~ 99 사이의 난수 30개
tall = np.round(np.random.normal(170, 10, size=30), 2)
# 평균:  170, 표준편차: 10 인 정규분포를 따르는 난수 30개. 대부분의 난수가 170-10*2 ~ 170+10*2 범위. 평균에 가까운 값들이 많이 생성.
# print(age)
# print(tall)
df = pd.DataFrame({
    '나이': age,
    '키': tall
})
df
나이
0 45 168.18
1 48 184.10
2 65 166.26
3 68 172.75
4 68 160.39
5 10 173.77
6 84 170.33
7 22 176.81
8 37 154.37
9 88 164.33
10 71 167.58
11 89 185.14
12 89 166.67
13 13 170.47
14 59 184.63
15 66 185.35
16 40 175.66
17 88 171.49
18 47 159.22
19 89 183.95
20 82 187.87
21 38 164.30
22 26 171.75
23 78 165.37
24 73 159.14
25 10 176.40
26 21 166.14
27 81 162.24
28 70 179.96
29 80 150.67
df.나이.value_counts()
89    3
68    2
10    2
88    2
45    1
47    1
70    1
81    1
21    1
73    1
78    1
26    1
38    1
82    1
59    1
40    1
66    1
48    1
13    1
71    1
37    1
22    1
84    1
65    1
80    1
Name: 나이, dtype: int64
df..value_counts()
168.18    1
184.10    1
179.96    1
162.24    1
166.14    1
176.40    1
159.14    1
165.37    1
171.75    1
164.30    1
187.87    1
183.95    1
159.22    1
171.49    1
175.66    1
185.35    1
184.63    1
170.47    1
166.67    1
185.14    1
167.58    1
164.33    1
154.37    1
176.81    1
170.33    1
173.77    1
160.39    1
172.75    1
166.26    1
150.67    1
Name: 키, dtype: int64

ex) 예제

  • (): closed - 불포함, []: openned - 포함 !!!!!!!
  • (36.333/62.667] => 36.333 < x <= 62.667
# 연속형 값 - > 볌주형 값(범위로 묶어서 같은 범주로 만든다.) ==> cut()/qcut()
# cut
나이대 = pd.cut(df['나이'],bins=3, #df['나이'] => 같은 범위로 3등분
             right = False,    # closed 방향을 지정. default : right = True
             labels=['나이대1', '나이대2', '나이대3']) # 각 범주의 label 을 지정.
나이대
# - (): closed - 불포함, []: openned - 포함 !!!!!!!
# - (36.333/62.667] => 36.333 < x <= 62.667
0     나이대2
1     나이대2
2     나이대3
3     나이대3
4     나이대3
5     나이대1
6     나이대3
7     나이대1
8     나이대2
9     나이대3
10    나이대3
11    나이대3
12    나이대3
13    나이대1
14    나이대2
15    나이대3
16    나이대2
17    나이대3
18    나이대2
19    나이대3
20    나이대3
21    나이대2
22    나이대1
23    나이대3
24    나이대3
25    나이대1
26    나이대1
27    나이대3
28    나이대3
29    나이대3
Name: 나이, dtype: category
Categories (3, object): ['나이대1' < '나이대2' < '나이대3']
df['나이대1'] = 나이대
df
나이 나이대1 나이대
0 45 168.18 나이대2 장년
1 48 184.10 나이대2 장년
2 65 166.26 나이대3 노년
3 68 172.75 나이대3 노년
4 68 160.39 나이대3 노년
5 10 173.77 나이대1 청소년
6 84 170.33 나이대3 노년
7 22 176.81 나이대1 청년
8 37 154.37 나이대2 청년
9 88 164.33 나이대3 노년
10 71 167.58 나이대3 노년
11 89 185.14 나이대3 노년
12 89 166.67 나이대3 노년
13 13 170.47 나이대1 청소년
14 59 184.63 나이대2 장년
15 66 185.35 나이대3 노년
16 40 175.66 나이대2 청년
17 88 171.49 나이대3 노년
18 47 159.22 나이대2 장년
19 89 183.95 나이대3 노년
20 82 187.87 나이대3 노년
21 38 164.30 나이대2 청년
22 26 171.75 나이대1 청년
23 78 165.37 나이대3 노년
24 73 159.14 나이대3 노년
25 10 176.40 나이대1 청소년
26 21 166.14 나이대1 청년
27 81 162.24 나이대3 노년
28 70 179.96 나이대3 노년
29 80 150.67 나이대3 노년
l = [0,20,40,60,90]
나이대2 = pd.cut(df['나이'], bins=l, # 나눈는 기준점을 지정.
              labels=['청소년', '청년', '장년', '노년'])

나이대2
            
0      장년
1      장년
2      노년
3      노년
4      노년
5     청소년
6      노년
7      청년
8      청년
9      노년
10     노년
11     노년
12     노년
13    청소년
14     장년
15     노년
16     청년
17     노년
18     장년
19     노년
20     노년
21     청년
22     청년
23     노년
24     노년
25    청소년
26     청년
27     노년
28     노년
29     노년
Name: 나이, dtype: category
Categories (4, object): ['청소년' < '청년' < '장년' < '노년']
df['나이대1']= 나이대2
df
나이 나이대1 나이대
0 45 168.18 장년 장년
1 48 184.10 장년 장년
2 65 166.26 노년 노년
3 68 172.75 노년 노년
4 68 160.39 노년 노년
5 10 173.77 청소년 청소년
6 84 170.33 노년 노년
7 22 176.81 청년 청년
8 37 154.37 청년 청년
9 88 164.33 노년 노년
10 71 167.58 노년 노년
11 89 185.14 노년 노년
12 89 166.67 노년 노년
13 13 170.47 청소년 청소년
14 59 184.63 장년 장년
15 66 185.35 노년 노년
16 40 175.66 청년 청년
17 88 171.49 노년 노년
18 47 159.22 장년 장년
19 89 183.95 노년 노년
20 82 187.87 노년 노년
21 38 164.30 청년 청년
22 26 171.75 청년 청년
23 78 165.37 노년 노년
24 73 159.14 노년 노년
25 10 176.40 청소년 청소년
26 21 166.14 청년 청년
27 81 162.24 노년 노년
28 70 179.96 노년 노년
29 80 150.67 노년 노년
df['나이대1'].value_counts()
df['나이대1'].value_counts(normalize=True)
노년     0.566667
청년     0.200000
장년     0.133333
청소년    0.100000
Name: 나이대1, dtype: float64
df['키'].mean()
170.843
df.groupby('나이대1')['키'].mean()
나이대1
청소년    173.546667
청년     168.171667
장년     174.032500
노년     170.558235
Name: 키, dtype: float64
  • qcut : 동일한 원소수(개수)로 나눈다.
키대 = pd.qcut(df['키'], # 나눌 대상
        q = 3,  # 몇 개로 나눌지
        labels = ['작으키','중간키','큰키']
            )
키대
0     중간키
1      큰키
2     중간키
3     중간키
4     작으키
5     중간키
6     중간키
7      큰키
8     작으키
9     작으키
10    중간키
11     큰키
12    중간키
13    중간키
14     큰키
15     큰키
16     큰키
17    중간키
18    작으키
19     큰키
20     큰키
21    작으키
22    중간키
23    작으키
24    작으키
25     큰키
26    작으키
27    작으키
28     큰키
29    작으키
Name: 키, dtype: category
Categories (3, object): ['작으키' < '중간키' < '큰키']
키대.value_counts()
작으키    10
중간키    10
큰키     10
Name: 키, dtype: int64
df['키대'] = 키대
df.groupby('키대')['나이'].mean()
키대
작으키    61.1
중간키    55.9
큰키     57.5
Name: 나이, dtype: float64

TODO

import os
os.getcwd()
'/Users/seokminlee/Desktop/mose/pandas_template'

1: data/diamonds.csv 를 읽어 DataFrame으로 만든다.

dia = pd.read_csv('data/diamonds.csv')
dia.shape
(53940, 10)

2: price 컬럼을 ‘고가’, ‘중가’, ‘저가’ 세개의 범주값을 가지는 “price_cate” 컬럼을 생성한다.

labels=['고가', '중가', '저가']
price_cate = pd.cut(dia['price'], 3,     
             labels=labels) 
price_cate
0        고가
1        고가
2        고가
3        고가
4        고가
         ..
53935    고가
53936    고가
53937    고가
53938    고가
53939    고가
Name: price, Length: 53940, dtype: category
Categories (3, object): ['고가' < '중가' < '저가']
pd.cut(dia['price'], [300,1000,10000,20000], # 원하는 범위로 나눌떄
      labels =labels
      ) 
dia['price_cate'] = price_cate
pd.qcut(dia['price'], 3, labels = labels) # 데이터수로 등분
dia.head()
carat cut color clarity depth table price x y z price_cate
0 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43 고가
1 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31 고가
2 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31 고가
3 0.29 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63 고가
4 0.31 Good J SI2 63.3 58.0 335 4.34 4.35 2.75 고가

3 가격대(price_cate) 별 carat의 평균을 조회

dia.groupby('price_cate')['carat'].mean()
price_cate
고가    0.627943
중가    1.362280
저가    1.885256
Name: carat, dtype: float64

4 가격대(price_cate)와 cut별 평균 가격(price)를 피봇테이블로 조회

dia.pivot_table(index = 'cut', columns = 'price_cate', values = 'price', aggfunc = 'mean')
price_cate 고가 중가 저가
cut
Fair 2976.492819 8891.044335 15176.404762
Good 2589.423161 8744.889273 15412.570213
Ideal 1991.838539 9019.390308 15422.528676
Premium 2513.585759 9045.209888 15391.247016
Very Good 2383.723178 8921.683740 15502.217323

5 cut, color, price_cate 별 carat의 평균을 피봇테이블로 조회

dia.pivot_table(index = ['price_cate', 'cut'], columns = 'color', values = 'carat', aggfunc = 'mean')
color D E F G H I J
price_cate cut
고가 Fair 0.807122 0.767424 0.791801 0.849462 0.977699 0.988750 1.067717
Good 0.670725 0.651305 0.689913 0.708228 0.730387 0.764122 0.854693
Ideal 0.502135 0.511158 0.529869 0.528951 0.617462 0.669981 0.792335
Premium 0.599525 0.591792 0.666792 0.629871 0.765683 0.794620 0.901903
Very Good 0.599320 0.577348 0.619161 0.614364 0.712640 0.760677 0.832754
중가 Fair 1.339412 1.443500 1.564483 1.807895 1.796226 1.784000 1.985000
Good 1.277551 1.256889 1.298312 1.349915 1.523187 1.630000 1.695397
Ideal 1.170843 1.198333 1.189853 1.193722 1.398540 1.533642 1.645655
Premium 1.264649 1.260486 1.268342 1.300076 1.525855 1.610519 1.716263
Very Good 1.226980 1.212459 1.193546 1.244620 1.489892 1.551324 1.707778
저가 Fair 2.145714 1.843333 1.957273 1.995000 2.218750 2.407778 2.691818
Good 1.626500 1.717097 1.809286 1.864048 2.093810 2.104643 2.242500
Ideal 1.430909 1.501395 1.609551 1.694875 1.947143 2.097365 2.229880
Premium 1.636000 1.715130 1.705594 1.793261 2.069171 2.127762 2.236724
Very Good 1.552857 1.740141 1.688632 1.735664 2.019664 2.100703 2.150377

4강 명령어 메모 .

f2.sample(frac=0.3) # 비율 (전체중 30%의 행만 추출)

컬럼() 삽입 - df.insert(삽입할 컬럼 순번, 컬럼이름, 값들)
df.insert(1, "cnt1평균", result['cnt1'])

['CANCELLED'].sum() # 취소된 개수

댓글남기기