그룹화하기

groupby 메소드는 데이터프레임의 같은 값을 하나로 묶어서 통계나 집계 결과를 확안하기 위해 사용한다. 예를 들어 기상 데이터를 날짜별, 도시별로 값을 묶어서 통계나 집계 결과를 확인할 수 있다.

  • Split: 그룹별로 데이터를 나눈다.
  • Apply: 각 그룹별로 집계 함수를 적용한다.
  • Combine: 그룹별 집계 결과를 하나로 합치는 과정이다.
import numpy as np
import pandas as pd
 
flight = pd.read_csv('Clean_Dataset.csv')
# aireline 칼럼 기준으로 그룹화하기
airline_group = flight.groupby('airline')
airline_group
<pandas.api.typing.DataFrameGroupBy object at 0x0000010EE82BBCB0>

그룹화를 수행하면 airline 기준으로 데이터들이 모일 것이라고 생각하지만, 수행한 결과는 데이터프레임으로 그룹까지 생성한 것이다. 따라서 그룹화된 결과를 확인하려면 groups 속성을 이용한다.

# 수행 결과 확인하기
airline_group.groups
{'AirAsia': [2, 18, 19, 27, 48, 141, 147, 148, 157, 265, 290, 325, 435, 450, 608, 609, 623, 706, 746, 782, 783, 784, 814, 978, 993, 1085, 1088, 1133, 1210, 1248, 1261, 1284, 1323, 1341, 1343, 1461, 1466, 1483, 1526, 1583, 1685, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, 1728, 1759, 1760, 1761, 1762, 1866, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1947, 1972, 1974, 1976, 1977, 1991, 1992, 2083, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2167, 2194, 2196, 2264, 2265, 2292, 2305, 2350, 2351, 2352, 2353, 2354, 2355, 2397, 2422, 2423, 2443, 2444, 2465, 2484, ...], 'Air_India': [16, 17, 23, 37, 40, 41, 42, 43, 44, 49, 50, 51, 52, 53, 60, 71, 74, 75, 82, 86, 87, 96, 98, 107, 108, 110, 113, 114, 115, 117, 142, 143, 144, 145, 146, 151, 152, 162, 163, 164, 166, 168, 173, 182, 189, 191, 193, 195, 196, 197, 198, 199, 207, 211, 223, 225, 229, 236, 237, 241, 242, 243, 247, 250, 266, 267, 268, 269, 272, 280, 291, 295, 303, 309, 314, 315, 316, 319, 321, 329, 333, 343, 352, 357, 361, 362, 363, 372, 376, 379, 380, 381, 385, 387, 388, 390, 391, 392, 393, 394, ...], 'GO_FIRST': [8, 9, 10, 11, 20, 21, 22, 30, 68, 72, 101, 112, 126, 127, 128, 129, 130, 131, 149, 150, 200, 201, 208, 212, 213, 222, 232, 235, 244, 245, 257, 258, 259, 260, 270, 271, 277, 278, 281, 296, 312, 313, 317, 327, 328, 342, 366, 368, 371, 384, 389, 413, 414, 415, 416, 417, 418, 436, 437, 438, 439, 440, 441, 442, 455, 465, 469, 490, 491, 496, 497, 535, 552, 586, 587, 588, 589, 590, 591, 610, 611, 612, 613, 614, 615, 616, 617, 627, 639, 640, 646, 668, 669, 672, 673, 675, 691, 762, 763, 764, ...], 'Indigo': [12, 13, 14, 15, 24, 26, 29, 31, 54, 55, 56, 57, 58, 67, 79, 81, 132, 133, 134, 135, 136, 137, 139, 140, 158, 159, 167, 170, 171, 172, 174, 175, 180, 181, 190, 224, 226, 228, 261, 262, 263, 264, 282, 283, 284, 285, 286, 287, 292, 297, 301, 302, 307, 308, 310, 323, 324, 332, 337, 348, 353, 354, 358, 359, 360, 419, 420, 421, 422, 424, 425, 426, 427, 428, 445, 446, 447, 451, 454, 456, 457, 458, 459, 466, 479, 488, 489, 522, 524, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 625, ...], 'SpiceJet': [0, 1, 28, 38, 39, 45, 80, 118, 119, 138, 160, 161, 194, 227, 251, 252, 253, 274, 275, 294, 306, 311, 331, 398, 399, 400, 401, 402, 403, 423, 444, 448, 449, 452, 520, 540, 563, 564, 565, 566, 567, 568, 569, 570, 619, 620, 621, 622, 624, 665, 738, 739, 740, 741, 742, 743, 744, 745, 799, 800, 802, 844, 877, 898, 919, 920, 921, 922, 923, 924, 925, 971, 974, 975, 1049, 1092, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1150, 1155, 1200, 1243, 1281, 1286, 1287, 1288, 1289, 1290, 1291, 1331, 1332, 1368, 1369, 1412, ...], 'Vistara': [3, 4, 5, 6, 7, 25, 32, 33, 34, 35, 36, 46, 47, 59, 61, 62, 63, 64, 65, 66, 69, 70, 73, 76, 77, 78, 83, 84, 85, 88, 89, 90, 91, 92, 93, 94, 95, 97, 99, 100, 102, 103, 104, 105, 106, 109, 111, 116, 120, 121, 122, 123, 124, 125, 153, 154, 155, 156, 165, 169, 176, 177, 178, 179, 183, 184, 185, 186, 187, 188, 192, 202, 203, 204, 205, 206, 209, 210, 214, 215, 216, 217, 218, 219, 220, 221, 230, 231, 233, 234, 238, 239, 240, 246, 248, 249, 254, 255, 256, 273, ...]}

데이터는 그룹화된 것을 확인할 수 있지만 가독성은 좋지 않다.

groupby 내부 메소드를 활용하여 데이터를 확인할 수 있다.

  • count : 데이터 개수
  • size : 집단별 크기
  • sum : 데이터의 합
  • mean, std, var : 평균, 표준편차, 분산 min, max : 최솟값, 최댓값
# 데이터 수 확인하기
airline_group.count()
Unnamed: 0flightsource_citydeparture_timestopsarrival_timedestination_cityclassdurationdays_leftprice
airline
AirAsia1609816098160981609816098160981609816098160981609816098
Air_India8089280892808928089280892808928089280892808928089280892
GO_FIRST2317323173231732317323173231732317323173231732317323173
Indigo4312043120431204312043120431204312043120431204312043120
SpiceJet90119011901190119011901190119011901190119011
Vistara127859127859127859127859127859127859127859127859127859127859127859

데이터프레임의 복수 개의 칼럼을 기준으로 groupby를 활용하면 다중 인덱싱(multi-indexing)을 설정할 수 있다.

# groupby를 활용하여 다중 인덱싱(multi-indexing) 설정하기
flight.groupby(['airline', 'arrival_time']).mean(numeric_only=True)
Unnamed: 0durationdays_leftprice
airlinearrival_time
AirAsiaAfternoon88816.9839187.49633526.9805074206.467836
Early_Morning99485.1087429.45459127.0888423632.676617
Evening80346.8913838.62867527.1191174539.570963
Late_Night105201.9487259.20583529.0718993828.437410
Morning107012.5376429.93410827.8573393846.900653
Night88657.4788618.74039127.4872024320.171700
Air_IndiaAfternoon162295.57204615.95456325.82714223426.571700
Early_Morning138717.49045517.03797127.53950118805.551542
Evening166211.38750815.87805624.94298124459.593397
Late_Night166665.45023913.21407724.30143528014.163158
Morning162496.96891016.41155725.65576722792.180401
Night164175.09025714.23034825.45173523683.386323
GO_FIRSTAfternoon90534.2208727.51089227.7067895511.206937
Early_Morning85682.95489810.14896127.5334575537.433272
Evening86610.5969358.28480127.4747956022.345547
Late_Night90382.4337657.95736929.2455005306.723182
Morning84289.6461439.63367626.2658465281.059399
Night87864.8102939.08700326.9713605808.054728
IndigoAfternoon106570.2549215.48355026.0180535458.553278
Early_Morning116218.7378795.91391429.2703984107.122586
Evening113633.0020716.25535325.2005875914.292555
Late_Night110790.7780035.65235027.7985534989.383792
Morning93504.6529534.38930926.8354364558.313037
Night114668.9995286.17653526.1141645371.779781
SpiceJetAfternoon96337.99407614.37131525.2796217116.303318
Early_Morning86212.10679613.39557620.7739255348.022191
Evening97825.64290813.82671424.3499647465.200285
Late_Night125755.5504397.61282923.3399125911.436404
Morning88454.21188112.86635224.6990105777.899010
Night87037.01926811.82586924.1299805898.403658
VistaraAfternoon176534.77682412.11440225.46940527905.873134
Early_Morning178997.08681615.12222326.59780328597.738475
Evening178147.12519113.67978825.93598031225.269687
Late_Night195942.54535010.05171026.93703129699.180243
Morning177197.11008814.91393725.78816130180.664694
Night177240.74061912.19135825.93959630773.372570

여러 개의 칼럼을 기준으로 groupby한 경우 loc을 이용하여 원하는 인덱스의 데이터만 가져올 수 있다.

# groupby 후 원하는 데이터만 가져오기
flight.groupby(['airline', 'arrival_time']).mean(numeric_only=True).loc[[('AirAsia', 'Evening')]]
Unnamed: 0durationdays_leftprice
airlinearrival_time
AirAsiaEvening80346.8913838.62867527.1191174539.570963

인덱스로 그룹화하기

인덱스를 설정하여 그룹화할 때도 groupby 메소드의 레벨(level) 설정이 가능하다.

  • set_index : 칼럼을 인덱스로 변경하는 경우에 사용
  • reset_index : 인덱스를 초기화
# set_index로 인덱스 설정하기
flight.set_index(['airline', 'arrival_time'])
Unnamed: 0flightsource_citydeparture_timestopsdestination_cityclassdurationdays_leftprice
airlinearrival_time
SpiceJetNight0SG-8709DelhiEveningzeroMumbaiEconomy2.1715953
Morning1SG-8157DelhiEarly_MorningzeroMumbaiEconomy2.3315953
AirAsiaEarly_Morning2I5-764DelhiEarly_MorningzeroMumbaiEconomy2.1715956
VistaraAfternoon3UK-995DelhiMorningzeroMumbaiEconomy2.2515955
Morning4UK-963DelhiMorningzeroMumbaiEconomy2.3315955
.................................
Evening300148UK-822ChennaiMorningoneHyderabadBusiness10.084969265
Night300149UK-826ChennaiAfternoononeHyderabadBusiness10.424977105
Night300150UK-832ChennaiEarly_MorningoneHyderabadBusiness13.834979099
Evening300151UK-828ChennaiEarly_MorningoneHyderabadBusiness10.004981585
Evening300152UK-822ChennaiMorningoneHyderabadBusiness10.084981585

300153 rows × 10 columns

Aggregate로 집계하기

aggregate 메소드를 활용하면 데이터프레임의 값을 다양하게 집계하여 한 번에 볼 수 있다.

# aggregate 메소드 이용하여 groupby 후 평균값과 최댓값 확인하기
flight.set_index(['airline', 'arrival_time']).groupby(level=[0,1]).aggregate([np.min, np.max])
Unnamed: 0flightsource_citydeparture_timestopsdestination_cityclassdurationdays_leftprice
minmaxminmaxminmaxminmaxminmaxminmaxminmaxminmaxminmaxminmax
airlinearrival_time
AirAsiaAfternoon27202598I5-1228I5-942BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy0.9218.33149144331917
Early_Morning2193744I5-1229I5-996BangaloreMumbaiEarly_MorningNightonezeroChennaiMumbaiEconomyEconomy0.9215.50149160329501
Evening10006205846I5-1228I5-991BangaloreMumbaiAfternoonMorningonezeroBangaloreMumbaiEconomyEconomy0.9214.58149144331799
Late_Night1693202571I5-1228I5-992BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.2517.33149144331707
Morning19206617I5-1228I5-992BangaloreMumbaiEarly_MorningNightonezeroBangaloreMumbaiEconomyEconomy1.0019.58149110526360
Night1323202569I5-1228I5-996BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy0.9218.00149144330211
Air_IndiaAfternoon37299989AI-402AI-9937BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.0044.50149183086491
Early_Morning8846300114AI-403AI-9928BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.3340.75149177680762
Evening110299935AI-401AI-9929BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.5049.83149183084374
Late_Night193295659AI-401AI-9929BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy2.5841.58149386389257
Morning16300146AI-401AI-9929BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.0845.83149152686491
Night23300147AI-401AI-9991BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.1741.83149183090970
GO_FIRSTAfternoon11206589G8-101G8-917BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0017.92149110531773
Early_Morning150202069G8-101G8-815BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0022.50149177625462
Evening9206183G8-101G8-917BangaloreMumbaiAfternoonMorningonezeroBangaloreMumbaiEconomyEconomy1.1715.50149110528174
Late_Night1521197767G8-102G8-962BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.2516.08149171416120
Morning8187519G8-101G8-812BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0020.67149171427620
Night21206320G8-101G8-917BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0817.00149171432803
IndigoAfternoon132066506E-1136E-982BangaloreMumbaiAfternoonMorningonezeroBangaloreMumbaiEconomyEconomy0.9210.92149110529213
Early_Morning16782066076E-1346E-987BangaloreMumbaiEarly_MorningNightonezeroBangaloreMumbaiEconomyEconomy0.8312.42149110524120
Evening142066146E-1026E-982BangaloreMumbaiAfternoonMorningonezeroBangaloreMumbaiEconomyEconomy0.9212.67149110530786
Late_Night7722066026E-1266E-992BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.1712.33149110525086
Morning122066106E-1216E-995BangaloreMumbaiEarly_MorningNightonezeroBangaloreMumbaiEconomyEconomy0.8312.50149110524047
Night292066156E-1026E-998BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy0.8315.42149110531952
SpiceJetAfternoon45193244SG-163SG-9974BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.2526.33149156620874
Early_Morning275206184SG-1031SG-8803BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0026.33144110625454
Evening194203920SG-197SG-9911BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0827.92149249127769
Late_Night10389187402SG-1057SG-8906BangaloreKolkataAfternoonNightonezeroBangaloreMumbaiEconomyEconomy2.5019.25149256820298
Morning1203820SG-1032SG-9923BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.0027.50149144434158
Night0202067SG-1062SG-958BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiEconomyEconomy1.7527.58149210026939
VistaraAfternoon3300133UK-613UK-996BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.5033.081492022115211
Early_Morning573300125UK-637UK-995BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.5026.084492074106416
Evening7300152UK-613UK-996BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.0037.581491776117307
Late_Night43306290332UK-772UK-986BangaloreMumbaiAfternoonNightonezeroDelhiDelhiBusinessEconomy2.1728.33349233983087
Morning4300138UK-613UK-996BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.1747.081491714111964
Night46300150UK-613UK-996BangaloreMumbaiAfternoonNightonezeroBangaloreMumbaiBusinessEconomy1.0040.001491714123071

피벗 테이블 생성하기

‘(축을 중심으로) 회전한다’는 사전적 의미처럼 pivot과 pivot_table 메소드는 행 데이터를 열 데이터로 회전할 수 있다.

# pivot/pivot_table을 위한 데이터프레임 만들기
pivot_data = pd.DataFrame({'cust_id': ['cust_1', 'cust_1', 'cust_1', 'cust_2', 'cust_2', 'cust_2', 'cust_3', 'cust_3', 'cust_3'],
                           'prod_cd': ['p1', 'p2', 'p3', 'p1', 'p2', 'p3', 'p1', 'p2', 'p3'],
                           'grade': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B'],
                           'purch_amt': [30, 10, 0, 40, 15, 30, 0, 0, 10]})
pivot_data
cust_idprod_cdgradepurch_amt
0cust_1p1A30
1cust_1p2A10
2cust_1p3A0
3cust_2p1A40
4cust_2p2A15
5cust_2p3A30
6cust_3p1B0
7cust_3p2B0
8cust_3p3B10

pivot을 사용하여 해당 데이터프레임을 변경해보자. pivot은 기본적으로 index, columns, values를 지정하여 구성할 수 있다.

# pivot 활용하기
pivot_data.pivot(index = 'cust_id', columns='prod_cd', values='purch_amt')
prod_cdp1p2p3
cust_id
cust_130100
cust_2401530
cust_30010

pivot과 pivot_table의 기능은 동일하지만, pivot_table은 pivot과 다르게 aggfunc를 지정할 수 있다. aggfunc을 지정할 때 중복값이 존재하는 경우 pivot은 사용할 수 없고 pivot_table만 사용할 수 있다.

try:
    pivot_data.pivot(index='grade', columns='prod_cd', values='purch_amt')
except Exception as e:
    print(f"Error occurred: {e}")
Error occurred: Index contains duplicate entries, cannot reshape

pivot을 수행하면 위와 같이 인덱스에 중복이 있어 변경할 수 없다는 에러 메시지가 나타난다. 실제 인덱스로 쓰인 grade는 중복값을 가지고 있다. 하지만 pivot_table을 동일하게 수행할 경우 평균값이 결과로 보여진다.

# pivot_table 활용하기
pivot_data.pivot_table(index='grade', columns='prod_cd', values='purch_amt')
prod_cdp1p2p3
grade
A35.012.515.0
B0.00.010.0

pivot_table에서 aggfunc의 기본은 평균값이고, 따로 지정할 수도 있다.

아래는 aggfunc을 합계로 지정한 예시이다.

# pivot_table의 aggfunc 지정하기
pivot_data.pivot_table(index='grade', columns='prod_cd', values='purch_amt', aggfunc=np.sum)
prod_cdp1p2p3
grade
A702530
B0010

인덱스 및 칼럼 레벨 변경하기

데이터프레임을 변형하는 또 다른 방법으로 stack/unstack 메소드를 알아보자. ‘쌓다’라는 의미로 stack은 칼럼 레벨에서 인덱스 레벨로 데이터프레임을 변경한다. 데이터를 행의 레벨로 쌓아 올리는 개념으로 이해하면 된다.

반대로 unstack은 인덱스 레벨에서 칼럼 레벨로 위치를 변경하여 데이터를 쌓아 올린다.

# stack/unstack을 위한 데이터프레임 만들기
stack_data = pd.DataFrame({
    'Location': ['Seoul', 'Seoul', 'Seoul', 'Kyounggi', 'Kyounggi', 'Busan', 'Seoul', 'Seoul', 'Busan', 'Kyounggi', 'Kyounggi', 'Kyounggi'],
    'Day': ['Mon', 'Tue', 'Wed', 'Mon', 'Tue', 'Mon', 'Thu', 'Fri', 'Tue', 'Wed', 'Thu', 'Fri'],
    'Ranfall': [100, 80, 1000, 200, 200, 100, 50, 100, 200, 100, 50, 100],
    'Rainfall_precipitation': [80, 70, 90, 10, 20, 30, 50, 90, 20, 80, 50, 10],
    'Temp': [32, 27, 32, 31, 30, 28, 27, 25, 26, 33, 34, 31]
    })
stack_data
LocationDayRanfallRainfall_precipitationTemp
0SeoulMon1008032
1SeoulTue807027
2SeoulWed10009032
3KyounggiMon2001031
4KyounggiTue2002030
5BusanMon1003028
6SeoulThu505027
7SeoulFri1009025
8BusanTue2002026
9KyounggiWed1008033
10KyounggiThu505034
11KyounggiFri1001031

Location과 Day를 인덱스로 설정하고 새로운 데이터프레임을 만든다.

# 인덱스 설정하기
new_stack_data = stack_data.set_index(['Location', 'Day'])
new_stack_data
RanfallRainfall_precipitationTemp
LocationDay
SeoulMon1008032
Tue807027
Wed10009032
KyounggiMon2001031
Tue2002030
BusanMon1003028
SeoulThu505027
Fri1009025
BusanTue2002026
KyounggiWed1008033
Thu505034
Fri1001031

unstack은 인덱스 레벨에서 칼럼 레벨로 위치를 변경하여 데이터를 쌓아 올리는 것이다.

# unstack 확인하기
new_stack_data.unstack(0)
RanfallRainfall_precipitationTemp
LocationBusanKyounggiSeoulBusanKyounggiSeoulBusanKyounggiSeoul
Day
FriNaN100.0100.0NaN10.090.0NaN31.025.0
Mon100.0200.0100.030.010.080.028.031.032.0
ThuNaN50.050.0NaN50.050.0NaN34.027.0
Tue200.0200.080.020.020.070.026.030.027.0
WedNaN100.01000.0NaN80.090.0NaN33.032.0

첫 번째 인덱스 Location이 칼럼 레벨로 올라간 것을 확인할 수 있다. 인덱스에도 레벨(Depth)이 있어서 unstack(1)을 수행해 보면 두 번째 인덱스였던 요일이 칼럼 레벨로 올라간다.