다운로드
작성자: admin 작성일시: 2016-07-08 23:34:29 조회수: 7060 다운로드: 272
카테고리: Python 태그목록:

피봇테이블과 그룹 분석

피봇 테이블

피봇 테이블(pivot table)이란 데이터 열 중에서 두 개를 키(key)로 사용하여 데이터를 선택하는 방법을 말한다. Pandas는 피봇 테이블을 만들기 위한 pivot 메서드를 제공한다. 첫번째 인수로는 행 인덱스로 사용할 열 이름, 두번째 인수로는 열 인덱스로 사용할 열 이름, 그리고 마지막으로 데이터로 사용할 열 이름을 넣는다.

Pandas는 지정된 두 열을 각각 행 인덱스와 열 인덱스로 바꾼 후 행 인덱스의 라벨 값이 첫번째 키의 값과 같고 열 인덱스의 라벨 값이 두번째 키의 값과 같은 데이터를 찾아서 해당 칸에 넣는다. 만약 주어진 데이터가 존재하지 않으면 해당 칸에 NaN 값을 넣는다.

In:
data = {
    "도시": [
        "서울", "서울", "서울", "부산", "부산", "부산", "인천", "인천"
    ],
    "연도": [
        "2015", "2010", "2005", "2015", "2010", "2005", "2015", "2010"
    ],
    "인구": [
        9904312, 9631482, 9762546, 3448737, 3393191, 3512547, 2890451, 2632035
    ],
    "지역": [
        "수도권", "수도권", "수도권", "경상권", "경상권", "경상권", "수도권", "수도권"
    ]
}
columns = ["도시", "연도", "인구", "지역"]
df1 = pd.DataFrame(data, columns=columns)
df1
Out:
도시 연도 인구 지역
0 서울 2015 9904312 수도권
1 서울 2010 9631482 수도권
2 서울 2005 9762546 수도권
3 부산 2015 3448737 경상권
4 부산 2010 3393191 경상권
5 부산 2005 3512547 경상권
6 인천 2015 2890451 수도권
7 인천 2010 2632035 수도권
In:
df1.pivot("도시", "연도", "인구")
Out:
연도 2005 2010 2015
도시
부산 3512547.0 3393191.0 3448737.0
서울 9762546.0 9631482.0 9904312.0
인천 NaN 2632035.0 2890451.0

이 피봇 테이블의 값 3512547은 "도시"가 부산이고 "연도"가 2005년인 데이터를 "인구"열에서 찾은 값이다. 2005년 인천의 인구는 데이터에 없기 때문에 NaN으로 표시된다.

사실 피봇 테이블은 다음과 같이 set_index 명령과 unstack 명령을 사용해서 만들 수도 있다.

In:
df1.set_index(["도시", "연도"])[["인구"]].unstack()
Out:
인구
연도 2005 2010 2015
도시
부산 3512547.0 3393191.0 3448737.0
서울 9762546.0 9631482.0 9904312.0
인천 NaN 2632035.0 2890451.0

행 인덱스와 열 인덱스는 하나의 데이터를 찾는 키(key)의 역할을 한다. 즉, 이 값으로 데이터가 유일하게(unique) 결정되어야 한다. 만약 행 인덱스와 열 인덱스 조건을 만족하는 데이터가 2개 이상인 경우에는 에러가 발생한다. 예를 들어 위 데이터프레임에서 ("지역", "연도")를 키로 하면 ("수도권", "2015")에 해당하는 값이 두 개 이상이므로 다음과 같이 에러가 발생한다.

In:
df1.pivot("지역", "연도", "인구")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
 in ()
----> 1 df1.pivot("지역", "연도", "인구")

~/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py in pivot(self, index, columns, values)
   3851         """
   3852         from pandas.core.reshape.reshape import pivot
-> 3853         return pivot(self, index=index, columns=columns, values=values)
   3854 
   3855     def stack(self, level=-1, dropna=True):

~/anaconda3/lib/python3.6/site-packages/pandas/core/reshape/reshape.py in pivot(self, index, columns, values)
    376         indexed = Series(self[values].values,
    377                          index=MultiIndex.from_arrays([index, self[columns]]))
--> 378         return indexed.unstack(columns)
    379 
    380 

~/anaconda3/lib/python3.6/site-packages/pandas/core/series.py in unstack(self, level, fill_value)
   2026         """
   2027         from pandas.core.reshape.reshape import unstack
-> 2028         return unstack(self, level, fill_value)
   2029 
   2030     # ----------------------------------------------------------------------

~/anaconda3/lib/python3.6/site-packages/pandas/core/reshape/reshape.py in unstack(obj, level, fill_value)
    456     else:
    457         unstacker = _Unstacker(obj.values, obj.index, level=level,
--> 458                                fill_value=fill_value)
    459         return unstacker.get_result()
    460 

~/anaconda3/lib/python3.6/site-packages/pandas/core/reshape/reshape.py in __init__(self, values, index, level, value_columns, fill_value)
    108 
    109         self._make_sorted_values_labels()
--> 110         self._make_selectors()
    111 
    112     def _make_sorted_values_labels(self):

~/anaconda3/lib/python3.6/site-packages/pandas/core/reshape/reshape.py in _make_selectors(self)
    146 
    147         if mask.sum() < len(self.index):
--> 148             raise ValueError('Index contains duplicate entries, '
    149                              'cannot reshape')
    150 

ValueError: Index contains duplicate entries, cannot reshape

그룹 분석

이렇게 특정 조건에 맞는 데이터가 하나 이상 즉, 그룹을 이루는 경우는 그룹 분석을 해야 한다.

그룹 분석은 피봇 테이블과 달리 키에 의해서 결정되는 데이터가 여러개가 있어도 괜찮다. 대신 주어진 연산을 통해 그룹 데이터의 대표값을 정하게 된다. Pandas에서는 groupby 명령과 그룹 연산 메서드를 이용하여 그룹 분석을 한다.

그룹 연산을 하는 방법은 다음과 같다.

  1. 분석하고자 하는 시리즈나 데이터프레임에 groupby 메서드를 호출하여 그룹화를 한다.
  2. 그룹 객체에 대해 그룹 연산을 수행한다.

groupby 메서드

groupby 메서드는 데이터를 그룹 별로 분류하는 역할을 한다. groupby 메서드의 인수로는 다음과 같은 값을 사용한다.

  • 열 또는 열의 리스트
  • 행 인덱스

연산 결과로 그룹 데이터를 나타내는 GroupBy 클래스 객체를 반환한다. 이 객체에는 그룹별로 연산을 할 수 있는 그룹 연산 메서드가 있다.

그룹 연산 메서드

groupby 결과, 즉 GroupBy 클래스 객체의 뒤에 붙일 수 있는 그룹 연산 메서드는 다양하다. 전체 목록을 보려면 다음 웹사이트를 참조한다.

다음은 자주 사용되는 그룹 연산 메서드들이다.

  • size(), count(): 갯수
  • mean(), median(), min(), max(): 평균, 중앙값, 최소, 최대
  • sum(), prod(), std(), var(), quantile() : 합계, 곱, 표준편차, 분산, 사분위수
  • first(), last(): 가장 첫번째 데이터와 가장 나중 데이터

이 외에도 많이 사용되는 것으로는 다음과 같은 그룹 연산이 있다.

  • agg(), aggregate()

    • 만약 원하는 그룹 연산이 없는 경우 함수를 만들고 이 함수를 agg()에 전달한다.
    • 또는 여러가지 그룹 연산을 동시에 하고 싶은 경우 함수 이름 문자열의 리스트를 전달한다.
  • describe()

    • 하나의 그룹 대표값이 아니라 여러개의 값을 데이터프레임으로 구한다.
  • apply()

    • describe() 처럼 하나의 대표값이 아닌 데이터프레임을 출력하지만 원하는 그룹 연산이 없는 경우에 사용한다.
  • transform()

    • 그룹에 대한 대표값을 만드는 것이 아니라 그룹별 계산을 통해 데이터 자체를 변형한다.

예를 들어 다음과 같은 데이터가 있을 때 key1 값에 따른 data1의 평균은 어떻게 구할까? 그룹 분석으로 이 결과를 구할 수 있다.

In:
np.random.seed(0)
df2 = pd.DataFrame({
    'key1': ['A', 'A', 'B', 'B', 'A'],
    'key2': ['one', 'two', 'one', 'two', 'one'],
    'data1': [1, 2, 3, 4, 5],
    'data2': [10, 20, 30, 40, 50]
})
df2
Out:
data1 data2 key1 key2
0 1 10 A one
1 2 20 A two
2 3 30 B one
3 4 40 B two
4 5 50 A one

우선 데이터프레임 전체에 대해 그룹분석을 하자. groupby 명령을 수행하고 그 결과에 대해 각각 합계를 구하기 위해 sum이라는 그룹 연산을 하였다.

In:
df2.groupby(df2.key1).sum()
Out:
data1 data2
key1
A 8 80
B 7 70

만약 열 data1에 대해서만 그룹 연산을 하고 싶다면 미리 시리즈를 구하거나 groupby 반환값 또는 최종 그룹연산 결과에서 data1만 뽑아도 된다.

In:
df2.data1.groupby(df2.key1).sum()
Out:
key1
A    8
B    7
Name: data1, dtype: int64
In:
df2.groupby(df2.key1)["data1"].sum()
Out:
key1
A    8
B    7
Name: data1, dtype: int64
In:
df2.groupby(df2.key1).sum()["data1"]
Out:
key1
A    8
B    7
Name: data1, dtype: int64

연습 문제 1

key1의 값을 기준으로 data1의 값을 분류하여 합계를 구한 결과를 시리즈가 아닌 데이터프레임으로 구한다.

이번에는 복합 키 (key1, key2) 값에 따른 data1의 합계를 구하자. 이 결과는 피봇 데이블과 유사하다. 분석하고자 하는 키가 복수이면 리스트를 사용한다.

In:
df2.data1.groupby([df2.key1, df2.key2]).sum()
Out:
key1  key2
A     one     6
      two     2
B     one     3
      two     4
Name: data1, dtype: int64

이 결과를 unstack 명령으로 피봇 데이블 형태로 만들면 더 보기가 좋아진다.

In:
df2.data1.groupby([df2["key1"], df2["key2"]]).sum().unstack("key2")
Out:
key2 one two
key1
A 6 2
B 3 4

그룹 분석 기능을 사용하면 위의 인구 데이터를 지역별로 합계를 계산할 수 있다.

In:
df1["인구"].groupby([df1["지역"], df1["연도"]]).sum().unstack("연도")
Out:
연도 2005 2010 2015
지역
경상권 3512547 3393191 3448737
수도권 9762546 12263517 12794763

다음 데이터는 150 송이의 붓꽃(iris)에 대해 붓꽃 종(species)별로 꽃잎길이(sepal_length), 꽃잎폭(sepal_width), 꽃잎폭(sepal_width), 꽃잎폭(sepal_width)을 측정한 데이터이다. (Seaborn 패키지가 설치되어 있어야 한다.

In:
import seaborn as sns
iris = sns.load_dataset("iris")

각 붓꽃 종별로 가장 큰 값과 가장 작은 값의 비율을 구해보자. 이러한 계산을 하는 그룹 연산 메서드는 없으므로 직접 만든 후 agg 메서드를 적용한다.

In:
def peak_to_peak_ratio(x):
    return x.max() / x.min()
In:
iris.groupby(iris.species).agg(peak_to_peak_ratio)
Out:
sepal_length sepal_width petal_length petal_width
species
setosa 1.348837 1.913043 1.900000 6.000000
versicolor 1.428571 1.700000 1.700000 1.800000
virginica 1.612245 1.727273 1.533333 1.785714

describe 메서드를 사용하면 다양한 기술 통계(descriptive statistics)값을 한 번에 구한다. 그룹별로 하나의 스칼라 값이 아니라 하나의 데이터프레임이 생성된다는 점에 주의하라.

In:
iris.groupby(iris.species).describe().T
Out:
species setosa versicolor virginica
petal_length count 50.000000 50.000000 50.000000
mean 1.462000 4.260000 5.552000
std 0.173664 0.469911 0.551895
min 1.000000 3.000000 4.500000
25% 1.400000 4.000000 5.100000
50% 1.500000 4.350000 5.550000
75% 1.575000 4.600000 5.875000
max 1.900000 5.100000 6.900000
petal_width count 50.000000 50.000000 50.000000
mean 0.246000 1.326000 2.026000
std 0.105386 0.197753 0.274650
min 0.100000 1.000000 1.400000
25% 0.200000 1.200000 1.800000
50% 0.200000 1.300000 2.000000
75% 0.300000 1.500000 2.300000
max 0.600000 1.800000 2.500000
sepal_length count 50.000000 50.000000 50.000000
mean 5.006000 5.936000 6.588000
std 0.352490 0.516171 0.635880
min 4.300000 4.900000 4.900000
25% 4.800000 5.600000 6.225000
50% 5.000000 5.900000 6.500000
75% 5.200000 6.300000 6.900000
max 5.800000 7.000000 7.900000
sepal_width count 50.000000 50.000000 50.000000
mean 3.428000 2.770000 2.974000
std 0.379064 0.313798 0.322497
min 2.300000 2.000000 2.200000
25% 3.200000 2.525000 2.800000
50% 3.400000 2.800000 3.000000
75% 3.675000 3.000000 3.175000
max 4.400000 3.400000 3.800000

apply 메서드를 사용하면 하나의 그룹에 대해 하나의 대표값(스칼라 값)을 구하는 게 아니라 describe 메서드처럼 그룹별로 데이터프레임을 만들 수 있다. 예를 들어 다음처럼 각 붓꽃 종별로 가장 꽃잎길이가 큰 3개의 데이터를 뽑아낼 수도 있다.

In:
def max3(df):
    return df.sort_values(by="petal_length")[:3]
In:
iris.groupby(iris.species).apply(max3)
Out:
sepal_length sepal_width petal_length petal_width species
species
setosa 22 4.6 3.6 1.0 0.2 setosa
13 4.3 3.0 1.1 0.1 setosa
14 5.8 4.0 1.2 0.2 setosa
versicolor 98 5.1 2.5 3.0 1.1 versicolor
93 5.0 2.3 3.3 1.0 versicolor
57 4.9 2.4 3.3 1.0 versicolor
virginica 106 4.9 2.5 4.5 1.7 virginica
126 6.2 2.8 4.8 1.8 virginica
138 6.0 3.0 4.8 1.8 virginica

transform 메서드는 그룹별 대표값을 만드는 것이 아니라 그룹별 계산을 통해 데이터 값 자체를 변화시킨다. 따라서 만들어진 데이터프레임의 크기는 원래 데이터프레임과 같다. 예를 들어 다음처럼 각 붓꽃이 해당 종 중에서 꽃잎길이가 대/중/소 어느 것에 해당되는지에 대한 데이터프레임을 만들 수도 있다.

In:
def q3cut(s):
    return pd.qcut(s, 3, labels=["소", "중", "대"])
    
iris["petal_length_class"] = iris.groupby(iris.species)["petal_length"].transform(q3cut)
iris[["petal_length", "petal_length_class"]].tail(10)
Out:
petal_length petal_length_class
140 5.6
141 5.1
142 5.1
143 5.9
144 5.7
145 5.2
146 5.0
147 5.2
148 5.4
149 5.1

연습 문제 2

붓꽃(iris) 데이터에서 붓꽃 종(species)별로 꽃잎길이(sepal_length), 꽃잎폭(sepal_width) 등의 평균을 구하라. 만약 붓꽃 종(species)이 표시되지 않았을 때 이 수치들을 이용하여 붓꽃 종을 찾아낼 수 있을지 생각하라.

pivot_table

Pandas는 pivot 명령과 groupby 명령의 중간 성격을 가지는 pivot_table 명령도 제공한다.

pivot_table 명령은 groupby 명령처럼 그룹 분석을 하지만 최종적으로는 pivot 명령처럼 피봇테이블을 만든다. 즉 groupby 명령의 결과에 unstack을 자동 적용하여 2차원적인 형태로 변형한다. 사용 방법은 다음과 같다.

  • pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, margins_name='All')
    • data: 분석할 데이터프레임 (메서드일 때는 필요하지 않음)
    • values: 분석할 데이터프레임에서 분석할 열
    • index: 행 인덱스로 들어갈 키 열 또는 키 열의 리스트
    • columns: 열 인덱스로 들어갈 키 열 또는 키 열의 리스트
    • aggfunc: 분석 메서드
    • fill_value: NaN 대체 값
    • margins: 오른쪽과 아래에 합계를 붙일지 여부
    • margins_name: 합계 열(행)의 이름

만약 조건에 따른 데이터가 유일하게 선택되지 않으면 그룹 연산을 하며 이 때 aggfunc 인수로 정의된 함수를 수행하여 대표값을 계산한다.

pivot_table를 메서드로 사용할 때는 객체 자체가 데이터가 되므로 data 인수가 필요하지 않다.

예를 들어 위에서 만들었던 피봇 테이블은 pivot_table 명령으로 다음과 같이 만들 수도 있다.

In:
df2.pivot_table("data1", "key1", "key2")
Out:
key2 one two
key1
A 3 2
B 3 4
In:
df2.pivot_table("data1", "key1", "key2", margins=True, margins_name="합계")
Out:
key2 one two 합계
key1
A 3.0 2.0 2.666667
B 3.0 4.0 3.500000
합계 3.0 3.0 3.000000

행 인덱스나 열 인덱스에 리스트를 넣으면 다중 인덱스 테이블을 만든다.

In:
df2.pivot_table("data1", index=["key1", "key2"])
Out:
data1
key1 key2
A one 3
two 2
B one 3
two 4

TIP 데이터 예제

식당에서 식사 후 내는 팁(tip)과 관련된 데이터를 이용하여 좀더 구체적으로 그룹 분석 방법을 살펴본다. 우선 Seaborn 패키지에 설치된 샘플 데이터를 로드한다(Seaborn 패키지가 설치되어 있어야 한다). 이 데이터프레임에서 각각의 컬럼은 다음을 뜻한다.

  • total_bill: 식사대금
  • tip: 팁
  • sex: 성별
  • smoker: 흡연/금연 여부
  • day: 요일
  • time: 시간
  • size: 인원
In:
tips = sns.load_dataset("tips")
tips.tail()
Out:
total_bill tip sex smoker day time size
239 29.03 5.92 Male No Sat Dinner 3
240 27.18 2.00 Female Yes Sat Dinner 2
241 22.67 2.00 Male Yes Sat Dinner 2
242 17.82 1.75 Male No Sat Dinner 2
243 18.78 3.00 Female No Thur Dinner 2

분석의 목표는 식사 대금 대비 팁의 비율이 어떤 경우에 가장 높아지지는 찾는 것이다. 우선 식사대금와 팁의 비율을 나타내는 tip_pct를 추가하자.

In:
tips['tip_pct'] = tips['tip'] / tips['total_bill']
tips.tail()
Out:
total_bill tip sex smoker day time size tip_pct
239 29.03 5.92 Male No Sat Dinner 3 0.203927
240 27.18 2.00 Female Yes Sat Dinner 2 0.073584
241 22.67 2.00 Male Yes Sat Dinner 2 0.088222
242 17.82 1.75 Male No Sat Dinner 2 0.098204
243 18.78 3.00 Female No Thur Dinner 2 0.159744

다음으로 각 열의 데이터에 대해 간단히 분포를 알아본다.

In:
tips.describe()
Out:
total_bill tip size tip_pct
count 244.000000 244.000000 244.000000 244.000000
mean 19.785943 2.998279 2.569672 0.160803
std 8.902412 1.383638 0.951100 0.061072
min 3.070000 1.000000 1.000000 0.035638
25% 13.347500 2.000000 2.000000 0.129127
50% 17.795000 2.900000 2.000000 0.154770
75% 24.127500 3.562500 3.000000 0.191475
max 50.810000 10.000000 6.000000 0.710345

그룹별 통계

우선 성별로 나누어 데이터 갯수를 세어본다.

In:
tips.groupby("sex").count()
Out:
total_bill tip smoker day time size tip_pct
sex
Male 157 157 157 157 157 157 157
Female 87 87 87 87 87 87 87

데이터 갯수의 경우 NaN 데이터가 없다면 모두 같은 값이 나올 것이다. 이 때는 size 명령을 사용하면 더 간단히 표시된다. size 명령은 NaN이 있어도 상관하지 않는다.

In:
tips.groupby("sex").size()
Out:
sex
Male      157
Female     87
dtype: int64

이번에는 성별과 흡연유무로 나누어 데이터의 갯수를 알아본다.

In:
tips.groupby(["sex", "smoker"]).size()
Out:
sex     smoker
Male    Yes       60
        No        97
Female  Yes       33
        No        54
dtype: int64

좀 더 보기 좋도록 피봇 데이블 형태로 바꿀 수도 있다.

In:
tips.pivot_table("tip_pct", "sex", "smoker", aggfunc="count", margins=True)
Out:
smoker Yes No All
sex
Male 60.0 97.0 157.0
Female 33.0 54.0 87.0
All 93.0 151.0 244.0

이제 성별과 흡연 여부에 따른 평균 팁 비율을 살펴본다.

In:
tips.groupby("sex")[["tip_pct"]].mean()
Out:
tip_pct
sex
Male 0.157651
Female 0.166491
In:
tips.groupby("smoker")[["tip_pct"]].mean()
Out:
tip_pct
smoker
Yes 0.163196
No 0.159328

pivot_table 명령을 사용할 수도 있다.

In:
tips.pivot_table("tip_pct", "sex")
Out:
tip_pct
sex
Male 0.157651
Female 0.166491
In:
tips.pivot_table("tip_pct", ["sex", "smoker"])
Out:
tip_pct
sex smoker
Male Yes 0.152771
No 0.160669
Female Yes 0.182150
No 0.156921
In:
tips.pivot_table("tip_pct", "sex", "smoker")
Out:
smoker Yes No
sex
Male 0.152771 0.160669
Female 0.182150 0.156921

여성 혹은 흡연자의 팁 비율이 높은 것을 볼 수 있다. 하지만 이 데이터에는 평균을 제외한 분산(variance) 등의 다른 통계값이 없으므로 describe 명령으로 여러가지 통계값을 한 번에 알아본다.

In:
tips.groupby("sex")[["tip_pct"]].describe()
Out:
tip_pct
count mean std min 25% 50% 75% max
sex
Male 157.0 0.157651 0.064778 0.035638 0.121389 0.153492 0.186240 0.710345
Female 87.0 0.166491 0.053632 0.056433 0.140416 0.155581 0.194266 0.416667
In:
tips.groupby("smoker")[["tip_pct"]].describe()
Out:
tip_pct
count mean std min 25% 50% 75% max
smoker
Yes 93.0 0.163196 0.085119 0.035638 0.106771 0.153846 0.195059 0.710345
No 151.0 0.159328 0.039910 0.056797 0.136906 0.155625 0.185014 0.291990
In:
tips.groupby(["sex", "smoker"])[["tip_pct"]].describe()
Out:
tip_pct
count mean std min 25% 50% 75% max
sex smoker
Male Yes 60.0 0.152771 0.090588 0.035638 0.101845 0.141015 0.191697 0.710345
No 97.0 0.160669 0.041849 0.071804 0.131810 0.157604 0.186220 0.291990
Female Yes 33.0 0.182150 0.071595 0.056433 0.152439 0.173913 0.198216 0.416667
No 54.0 0.156921 0.036421 0.056797 0.139708 0.149691 0.181630 0.252672

연습 문제 3

  1. 팁의 비율이 요일과 점심/저녁 여부, 인원수에 어떤 영향을 받는지 살펴본다.
  2. 어떤 요인이 가장 크게 작용하는지 판단할 수 있는 방법이 있는가?

이번에는 각 그룹에서 가장 많은 팁과 가장 적은 팁의 차이를 알아보자. 이 계산을 해 줄 수 있는 그룹 연산 함수가 없으므로 함수를 직접 만들고 agg 메서드를 사용한다.

In:
def peak_to_peak(x):
    return x.max() - x.min()

tips.groupby(["sex", "smoker"])[["tip"]].agg(peak_to_peak)
Out:
tip
sex smoker
Male Yes 9.00
No 7.75
Female Yes 5.50
No 4.20

만약 여러가지 그룹 연산을 동시에 하고 싶다면 다음과 같이 리스트를 이용한다.

In:
tips.groupby(["sex", "smoker"]).agg(["mean", peak_to_peak])[["total_bill"]]
Out:
total_bill
mean peak_to_peak
sex smoker
Male Yes 22.284500 43.56
No 19.791237 40.82
Female Yes 17.977879 41.23
No 18.105185 28.58

만약 데이터 열마다 다른 연산을 하고 싶다면 열 라벨과 연산 이름(또는 함수)를 딕셔너리로 넣는다.

In:
tips.groupby(["sex", "smoker"]).agg({'tip_pct' : 'mean', 'total_bill' : peak_to_peak})
Out:
tip_pct total_bill
sex smoker
Male Yes 0.152771 43.56
No 0.160669 40.82
Female Yes 0.182150 41.23
No 0.156921 28.58

다음은 pivot_table 명령으로 더 복잡한 분석을 한 예이다.

In:
tips.pivot_table(['tip_pct', 'size'], ['sex', 'day'], 'smoker')
Out:
size tip_pct
smoker Yes No Yes No
sex day
Male Thur 2.300000 2.500000 0.164417 0.165706
Fri 2.125000 2.000000 0.144730 0.138005
Sat 2.629630 2.656250 0.139067 0.162132
Sun 2.600000 2.883721 0.173964 0.158291
Female Thur 2.428571 2.480000 0.163073 0.155971
Fri 2.000000 2.500000 0.209129 0.165296
Sat 2.200000 2.307692 0.163817 0.147993
Sun 2.500000 3.071429 0.237075 0.165710
In:
tips.pivot_table('size', ['time', 'sex', 'smoker'], 'day', 
                 aggfunc='sum', fill_value=0)
Out:
day Thur Fri Sat Sun
time sex smoker
Lunch Male Yes 23 5 0 0
No 50 0 0 0
Female Yes 17 6 0 0
No 60 3 0 0
Dinner Male Yes 0 12 71 39
No 0 4 85 124
Female Yes 0 8 33 10
No 2 2 30 43

연습 문제 4

타이타닉 승객 데이터를 이용하여 다음 분석을 실시하라. 데이터는 다음과 같이 받을 수 있다.

titanic = sns.load_dataset("titanic")
  1. qcut 명령으로 세 개의 나이 그룹을 만든다.
  2. 성별, 선실, 나이 그룹에 의한 생존율을 데이터프레임으로 계산한다. 행에는 성별 및 나이 그룹에 대한 다중 인덱스를 사용하고 열에는 선실 인덱스를 사용한다.
  3. 성별 및 선실에 의한 생존율을 피봇 데이터 형태로 만든다.

질문/덧글

그룹 연산 질문드립니다. lucc*** 2016년 9월 19일 9:20 오후

그룹 연산 중에 최빈값을 구하는 것은 없어서, 인터넷에 흘러다니는 정보들을 조합해서 이런 예시를 만들었습니다.

import pandas as pd
data = {
'test' : [1,1,1,1,2,2,2,2],
'result' : [10,10,20,20,30,30,40,40]}
df = pd.DataFrame(data)
most = df.result.groupby(df.test).apply(pd.Series.mode)
print(most)

결과
test
1 0 10
1 20
2 0 30
1 40

결과가 위와 같이 나오는데, 둘 중에 하나를 고를 수 있는 방법이 있나요?

예시)
1 1 20
2 1 40

답변: 그룹 연산 질문드립니다. 관리자 2016년 9월 20일 8:04 오전

다음과 같이 해보세요.

def mode(x):
return sp.stats.mode(x)[0][0]

most = df.result.groupby(df.test).agg(mode)

margins 에 대해 질문드립니다. kkn1*** 2017년 10월 9일 5:26 오후

margins을 통해 피봇테이블에서 오른쪽과 아래에 합을 구한다고 되어있는데
예제에서 출력된 값이 어떤 합인지에 대해 질문드립니다!
(1.815805 0.400157 1.343923 <- 이부분)