[AICE Associate] 기초 데이터 다루기

castor·2025년 4월 10일

AICE Associate

목록 보기
3/8

책 예제, 임의로 예제 만든 것 섞어서 사용합니다.
책: AICE Associate

Chapter 04 기초 데이터 다루기

Section 01 필요 데이터 선택하기

01) 칼럼명으로 데이터 선택하기

df[['칼럼명1', '칼럼명2', ...]] # 한 개도 가능

+) df[]와 df[[]]의 차이

print(type(df[''])) # <class 'pandas.core.series.Series'>
print(type(df[['']])) # <class 'pandas.core.frame.DataFrame'>

팬시 인덱싱(Fancy Indexing)

  • 특정 인덱스의 위치를 지정하는 형태의 리스트를 인덱싱 조건으로 적용하는 것
  • 비연속적인 여러 개의 값을 가져올 수 있다.
    인덱싱 집합을 리스트 또는 넘파이 ndarray 형태로 지정해 해당 위치에 있는 값을 가져올 수 있다.
    ex)
# 데이터 프레임 팬시인덱싱
import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})
print(df[['b', 'a']])
# b	 a
# 0	 4	1
# 1	 5	2
# 2	 6	3
# 넘파이 팬시인덱싱
import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
arr = arr.reshape(3, 3)
print(arr[[2], 0:2]) # array([[7, 8]])

02) 행 범위를 지정하여 데이터 선택하기
특정 행 범위에서 데이터를 가져올 때 슬라이싱(Slicing)을 이용해 가져올 수 있다.

슬라이싱(Slicing)
: 연속된 데이터를 확인하는데 유용하며 인덱스와 마찬가지로 순서가 있는 자료 구조에서 사용 가능하다.

df[1:2]
#    a	 b	 c
# 1	 2	 5	 8

03) 특정 행, 열 범위를 선택해 데이터 선택하기

loc(location)iloc(integer location)
데이터프레임의 행이나 열에 레이블로 접근데이터프레임의 행이나 열에 인덱스값으로 접근
인덱스 및 칼럼명을 통해 지정하는 방법인덱스를 활용해 지정하는 방법
설정한 인덱스를 그대로 사용0 based index로 사용

차이를 구분하기 위해 인덱스를 재설정을 한다.

# 기존 인덱스 확인
print(df.index) # RangeIndex(start=0, stop=3, step=1)
# 인덱스 새로 지정하기
df.index = np.arrange(101, 104) # 혹은 df.index = df.index + 101
print(df.index) # RangeIndex(start=101, stop=104, step=1)
# loc: 기존 인덱스를 사용할 수 있다.
df.loc[[101, 102]]
#       a	b	c
# 101	1	4	7
# 103	3	6	9
# iloc: 0부터 시작하는 인덱스를 사용해야한다.
df.iloc[[0, 2]]
# 	    a	b	c
# 101	1	4	7
# 103	3	6	9

행과 열을 동시에 사용하는 경우 loc와 iloc

101, 103행과 a, c열 조회

# loc
df.loc[[101, 103], ['a', 'b']]
#       a	b
# 101	1	4
# 103	3	6
# iloc
df.iloc[[0, 2], [0, 1]]
# 	    a	b
# 101	1	4
# 103	3	6

04) 조건으로 데이터 선택하기
Boolean 연산으로 원하는 데이터만 추출 가능하다.

# 데이터 프레임 새로 생성
import pandas as pd
from random import randrange, choice

num = [i + 1 for i in range(25)]
obj = [chr(randrange(65, 70)) for _ in range(len(num))]
color = [choice(['red', 'green', 'blue']) for _ in range(len(num))]
df = pd.DataFrame({'num': num, 'obj': obj, 'color': color})
# boolean 연산으로 조건을 만족하는 데이터만 추출하기
## num가 5 미만이고, color가 red인 항목만 추출
df[(df['num'] < 5) & (df['color'] == 'red')]
# 	num	obj	color
# 1	 2	B	red
# 2	 3	A	red

조건을 한번에 전부 넣어서 사용할 수 있지만, 조건을 자주 변경해야하거나 너무 많을 경우 조건을 변수로 저장해 사용할 수 있다.

# num가 5 미만이고, color가 red인 항목만 추출
num_tag = df['num'] < 5
color_tag = df['color'] == 'red'
df[num_tag & color_tag]#.head()
# 	num	obj	color
# 1	 2	B	red
# 2	 3	A	red

Section 02 필요 데이터 변경하기

01) 데이터 추가하기

# 기존 데이터프레임 컬럼을 이용해서 새 컬럼 추가
df['num2'] = df['num'] * 2 # num 칼럼 두 배한 칼럼 추가
print(df.head(3))
#      num	obj	color	num2
# 0 	1	B	blue	 2
# 1 	2	B	red	     4
# 2 	3	A	red	     6
# 생성한 칼럼으로 새 컬럼 생성
df['num_sum'] = df['num'] + df['num2'] # num 칼럼 두 배한 칼럼 추가
print(df.head(3))
df['num_sum'] = df['num'] + df['num2']
print(df.head(3))
#    num obj color  num2  num_sum
# 0    1   B  blue     2        3
# 1    2   B   red     4        6
# 2    3   A   red     6        9

데이터 삽입 - insert 사용

df.insert(loc, column, value, allow_deplicates=False)
# loc: 삽입될 열 위치
# column: 삽입될 열의 이름
# value: 삽입될 열의 값
# allow_duplicates: 중복 열의 삽입 허용할지 (True/False)
df.insert(5, 'num3', df['num2']*df['num_sum'])
print(df.head(3))
#    num obj color  num2  num_sum  num3
# 0    1   B  blue     2        3     6
# 1    2   B   red     4        6    24
# 2    3   A   red     6        9    54

02) 데이터 삭제하기

axis = 1: 열 레벨로 데이터를 삭제
axis = 0: 행 레벨로 데이터를 삭제

df.drop 파라미터

# 열(칼럼) 삭제 후 데이터프레임 저장
df = df.drop('num3', axis=1).head(3)
## 같은 변수명을 쓸거면 df.drop('num3', axis=1, inplace=True)을 사용해도 됨
print(df)
#    num obj color  num2  num_sum
# 0    1   B  blue     2        3
# 1    2   B   red     4        6
# 2    3   A   red     6        9
# 행 삭제
print(df.drop(index=24, axis=0).tail(3))
#      num obj color  num2  num_sum
# 21	22	C	green	44	66
# 22	23	D	blue	46	69
# 23	24	A	green	48	72

03) 칼럼명 변경하기

# rename 사용해서 칼럼명 변경하기
df = df.rename(columns={'num':'num1', 'obj':'level'})
print(df.head(3))
#    num1 level color  num2  num_sum
# 0     1     B  blue     2        3
# 1     2     B   red     4        6
# 2     3     A   red     6        9

04) 데이터프레임 정렬하기
df.sort_values 파라미터

# num1 칼럼 기준 역순 정렬 ascending/defalt = True
df.sort_values(by='num1', ascending=False).head(3)
#     num1	level	color	num2	num_sum
# 24	25	E	    blue	50	    75
# 23	24	A	    green	48      72
# 22	23	D	    blue	46	    69

Section 03 데이터 변형하기

01) 그룹화하기
(1) groupby 활용하기

그룹화 과정
- split: 그룹별로 데이터 나눈다.
- apply: 각 그룹별로 집계함수 적용한다.
- combine: 그룹별 집계 결과를 하나로 합친다.

책 예제 데이터

flight = pd.read_csv(r'.\Clean_Dataset.csv', encoding='cp949')
flight.head(3)
#   Unnamed: 0	airline	flight	source_city	departure_time	stops	arrival_time	destination_city	class	duration	days_left	price
# 0	0	       SpiceJet	SG-8709	Delhi	    Evening	        zero	Night	        Mumbai	            Economy	 2.17	    1	        5953
# 1	1	       SpiceJet	SG-8157	Delhi	    Early_Morning	zero	Morning	        Mumbai	            Economy	 2.33	    1	        5953
# 2	2	       AirAsia	I5-764	Delhi	    Early_Morning	zero	Early_Morning	Mumbai	            Economy	 2.17	    1	        5956

airline 칼럼 기준으로 그룹화하기

airline_group = flight.groupby('airline')
print(airline_group) # <pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000015D6D316EA0>

df.groupby는 데이터프레임으로 그룹까지 생성한 것이다. 그룹화된 결과를 확인하려면 .groups 속성을 사용해야한다.

print(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: 최솟값, 최댓값

count

airline_group.count()
#        Unnamed: 0	flight	source_city	departure_time	stops	arrival_time	destination_city	class	duration	days_left	price
# airline											
# AirAsia	16098	16098	16098	    16098	        16098	16098	        16098	            16098	16098	    16098	    16098
# Air_India	80892	80892	80892	    80892	        80892	80892	        80892	            80892	80892	    80892	    80892
# GO_FIRST	23173	23173	23173	    23173	        23173	23173	        23173	            23173	23173	    23173	    23173
# Indigo	43120	43120	43120	    43120	        43120	43120	        43120	            43120	43120	    43120	    43120
# SpiceJet	9011	9011	9011	    9011	        9011	9011	        9011	            9011	9011	    9011	    9011
# Vistara	127859	127859	127859	    127859	        127859	127859	        127859	            127859	127859	    127859	    127859

airline별 최솟값 확인

airline_group.min()

#   Unnamed: 0	flight	source_city	departure_time	stops	arrival_time	destination_city	class	duration	days_left	price
# airline											
# AirAsia	2	I5-1228	Bangalore	Afternoon	    one	    Afternoon	    Bangalore	        Economy	 0.92	    1	        1105
# Air_India	16	AI-401	Bangalore	Afternoon	    one	    Afternoon	    Bangalore	        Business 1.00	    1	        1526
# GO_FIRST	8	G8-101	Bangalore	Afternoon	    one	    Afternoon	    Bangalore	        Economy	 1.00	    1	        1105
# Indigo	12	6E-102	Bangalore	Afternoon	    one	    Afternoon	    Bangalore	        Economy	 0.83	    1	        1105
# SpiceJet	0	SG-1031	Bangalore	Afternoon	    one	    Afternoon	    Bangalore	        Economy	 1.00	    1	        1106
# Vistara	3	UK-613	Bangalore	Afternoon	    one	    Afternoon	    Bangalore	        Business 1.00	    1	        1714

airline별 수치형 데이터의 평균값

airline_group.mean(numeric_only=True)
# 	        Unnamed: 0	    duration	days_left	price
# airline				
# AirAsia	95102.971922	8.941714	27.735184	4091.072742
# Air_India	162945.107007	15.504235	25.497466	23507.019112
# GO_FIRST	87630.797005	8.755380	27.430415	5652.007595
# Indigo	110102.972658	5.795197	26.264309	5324.216303
# SpiceJet	91878.408834	12.579767	24.122850	6179.278881
# Vistara	177755.288310	13.326634	25.894532	30396.536302
# 특정 컬럼 평균
airline_group.mean(numeric_only=True)['price']
# airline
# AirAsia       4091.072742
# Air_India    23507.019112
# GO_FIRST      5652.007595
# Indigo        5324.216303
# SpiceJet      6179.278881
# Vistara      30396.536302
# Name: price, dtype: float64

여러 개 칼럼을 기준으로 groupby를 실행해다중 인덱싱을 할 수 있다.

flight.groupby(['airline', 'arrival_time']).mean(numeric_only=True)
# 		                    Unnamed: 0	    duration	days_left	price
# airline	  arrival_time				
# AirAsia	  Afternoon	    88816.983918	7.496335	26.980507	4206.467836
#             Early_Morning	99485.108742	9.454591	27.088842	3632.676617
#             Evening	    80346.891383	8.628675	27.119117	4539.570963
#             Late_Night	105201.948725	9.205835	29.071899	3828.437410
#             Morning	    107012.537642	9.934108	27.857339	3846.900653
#             Night	        88657.478861	8.740391	27.487202	4320.171700
# Air_India	  Afternoon	    162295.572046	15.954563	25.827142	23426.571700
#             Early_Morning	138717.490455	17.037971	27.539501	18805.551542
#             Evening	    166211.387508	15.878056	24.942981	24459.593397
#             Late_Night	166665.450239	13.214077	24.301435	28014.163158
#             Morning	    162496.968910	16.411557	25.655767	22792.180401
#             Night	        164175.090257	14.230348	25.451735	23683.386323
# GO_FIRST    Afternoon	    90534.220872	7.510892	27.706789	5511.206937
#             Early_Morning	85682.954898	10.148961	27.533457	5537.433272
#             Evening	    86610.596935	8.284801	27.474795	6022.345547
#             Late_Night	90382.433765	7.957369	29.245500	5306.723182
#             Morning	    84289.646143	9.633676	26.265846	5281.059399
#             Night	        87864.810293	9.087003	26.971360	5808.054728

여러 개 칼럼을 groupby 해서 새 데이터프레임 생성하기

flight.groupby(['airline', 'arrival_time']).mean(numeric_only=True).loc[[('AirAsia', 'Evening')]]
# 	                       Unnamed: 0	duration	days_left	price
# airline	arrival_time				
# AirAsia	Evening	     80346.891383	8.628675	27.119117	4539.570963

(2) 인덱스로 그룹화하기
인덱스를 설정해 그룹화할 때도 마찬가지로 groupby 메소드에 레벨(level) 설정이 가능

인덱스 관련 메소드
- set_index: 칼럼을 인덱스로 변경하는 경우 사용 기존 인덱스를 제거하고 칼럼 중 하나를 인덱스로 설정
- reset_index: 인덱스를 초기화
# set_index로 기존 칼럼 인덱스 설정하기
flight.set_index(['airline', 'arrival_time'])
#                           Unnamed: 0	flight	source_city	departure_time	stops	destination_city	class	duration	days_left	price
# airline	arrival_time										
# SpiceJet	Night	        0	        SG-8709	Delhi	    Evening	        zero	Mumbai	            Economy	 2.17	    1	        5953
#           Morning	        1	        SG-8157	Delhi	    Early_Morning	zero	Mumbai	            Economy	 2.33	    1	        5953
# AirAsia	Early_Morning	2	        I5-764	Delhi	    Early_Morning	zero	Mumbai	            Economy	 2.17	    1	        5956
#        	Afternoon	    3	        UK-995	Delhi	    Morning	        zero	Mumbai	            Economy	 2.25	    1	        5955
#           Morning	        4	        UK-963	Delhi	    Morning	        zero	Mumbai	            Economy	 2.33	    1	        5955
# ...	   ...	            ...	        ...	    ...	        ...	            ...	    ...	                ...	    ...	       ...          ...
# Vistara   Evening	        300148	    UK-822	Chennai	    Morning	        one	    Hyderabad	        Business 10.08	    49	        69265
#           Night	        300149	    UK-826	Chennai	    Afternoon	    one	    Hyderabad	        Business 10.42	    49	        77105
#           Night	        300150	    UK-832	Chennai	    Early_Morning	one	    Hyderabad	        Business 13.83	    49	        79099
#           Evening	        300151	    UK-828	Chennai	    Early_Morning	one	    Hyderabad	        Business 10.00	    49	        81585
#           Evening	        300152	    UK-822	Chennai	    Morning	        one	    Hyderabad	        Business 10.08	    49	        81585
# 300153 rows × 10 columns

실행결과를 보면 인덱스를 설정할 뿐 그룹화를 수행한 결과가 아님을 알 수 있다.

set_index로 인덱스 설정 후 인덱스 중 하나를 기준으로 그룹화하기

# 다중 인덱스(multi-index) 세팅 후 해당 인덱스 기준으로 groupby하기
flight.set_index(['airline', 'arrival_time']).groupby(level=[0]).mean(numeric_only=True)
# 	        Unnamed: 0	    duration	days_left	price
# airline				
# AirAsia	95102.971922	8.941714	27.735184	4091.072742
# Air_India	162945.107007	15.504235	25.497466	23507.019112
# GO_FIRST	87630.797005	8.755380	27.430415	5652.007595
# Indigo	110102.972658	5.795197	26.264309	5324.216303
# SpiceJet	91878.408834	12.579767	24.122850	6179.278881
# Vistara	177755.288310	13.326634	25.894532	30396.536302

airline_group.mean(numeric_only=True)과 비교해 보면 동일한 데이터를 설정했고, 레벨(depth)를 0으로 설정한 인덱스 중 airline을 기준으로 그룹화한 것을 확인할 수 있다.

# 인덱스를 모두 선택해 groupby 실행
flight.set_index(['airline', 'arrival_time']).groupby(level=[0, 1]).mean(numeric_only=True)
#                          Unnamed: 0	 duration	days_left	price
# airline	arrival_time				
# AirAsia	Afternoon	   88816.983918	 7.496335	26.980507	4206.467836
#           Early_Morning  99485.108742	 9.454591	27.088842	3632.676617
#           Evening	       80346.891383	 8.628675	27.119117	4539.570963
#           Late_Night	   105201.948725 9.205835	29.071899	3828.437410
#           Morning	       107012.537642 9.934108	27.857339	3846.900653
#           Night	       88657.478861	 8.740391	27.487202	4320.171700
# Air_India	Afternoon	   162295.572046 15.954563	25.827142	23426.571700
#           Early_Morning  138717.490455 17.037971	27.539501	18805.551542
#           Evening	       166211.387508 15.878056	24.942981	24459.593397
#           Late_Night	   166665.450239 13.214077	24.301435	28014.163158
#           Morning	       162496.968910 16.411557	25.655767	22792.180401
#           Night	       164175.090257 14.230348	25.451735	23683.386323
# GO_FIRST	Afternoon	   90534.220872	 7.510892	27.706789	5511.206937
#           ...	           ...	         ...	    ...	        ...

(3) Aggregate로 집계하기
aggregate로 데이터프레임 값을 다양하게 집계해 한 번에 확인할 수 있다.

numeric = flight.select_dtypes(include='number').columns
flight.set_index(['airline', 'arrival_time'])[numeric].groupby(level=[0, 1]).aggregate(['mean', 'max'])
#                                 Unnamed: 0	    duration	     days_left	         price
#                                 mean  max	        mean  max	     mean max	        mean max
# airline	arrival_time								
# AirAsia	Afternoon	  88816.983918  202598	7.496335  18.33	26.980507 49	 4206.467836 31917
#           Early_Morning 99485.108742  193744	9.454591  15.50	27.088842 49	 3632.676617 29501
#           Evening	      80346.891383  205846	8.628675  14.58	27.119117 49	 4539.570963 31799
#           Late_Night	  105201.948725 202571	9.205835  17.33	29.071899 49	 3828.437410 31707
#           Morning	      107012.537642	206617	9.934108  19.58	27.857339 49	 3846.900653 26360
#           Night	      88657.478861	202569	8.740391  18.00	27.487202 49	 4320.171700 30211
# Air_India	Afternoon	  162295.572046	299989	15.954563 44.50	25.827142 49	23426.571700 86491
#           Early_Morning 138717.490455	300114	17.037971 40.75	27.539501 49	18805.551542 80762
#           Evening	      166211.387508	299935	15.878056 49.83	24.942981 49	24459.593397 84374
#           Late_Night	  166665.450239	295659	13.214077 41.58	24.301435 49	28014.163158 89257
#           Morning	      162496.968910	300146	16.411557 45.83	25.655767 49	22792.180401 86491
#           Night	      164175.090257	300147	14.230348 41.83	25.451735 49	23683.386323 90970
# GO_FIRST	Afternoon	  90534.220872	206589	7.510892  17.92	27.706789 49	 5511.206937 31773
#           Early_Morning 85682.954898	202069	10.148961 22.50	27.533457 49	 5537.433272 25462
#           Evening	      86610.596935	206183	8.284801  15.50	27.474795 49	 6022.345547 28174
#           Late_Night	  90382.433765	197767	7.957369  16.08	29.245500 49	 5306.723182 16120
#           Morning	      84289.646143	187519	9.633676  20.67	26.265846 49	 5281.059399 27620
#           Night	      87864.810293	206320	9.087003  17.00	26.971360 49	 5808.054728 32803
#           ...	          ...	        ...	    ...	      ...   ...       ...    ...         ...

02) 피벗테이블 생성하기
'(축을 중심으로) 회전한다.'는 사전적 의미처럼 pivot과 pivot_tabel 메소드는 행 데이터를 열 데이터로 회전할 수 있다.

# 테이블 생성
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_id	prod_cd	grade	purch_amt
# 0	cust_1	p1	    A	    30
# 1	cust_1	p2	    A	    10
# 2	cust_1	p3	    A	    0
# 3	cust_2	p1	    A	    40
# 4	cust_2	p2	    A	    15
# 5	cust_2	p3	    A	    30
# 6	cust_3	p1	    B	    0
# 7	cust_3	p2	    B	    0
# 8	cust_3	p3	    B	    10
# 데이터 변경하기_pivot
pivot_data.pivot(index='cust_id', columns='prod_cd', values='purch_amt')

# prod_cd	p1	p2	p3
# cust_id			
# cust_1	30	10	0
# cust_2	40	15	30
# cust_3	0	0	10

# index와 colums, values를 지정하지 않을 경우 차례대로 index, colums, values으로 인식한다.
pivot_data.pivot('cust_id','prod_cd', 'purch_amt')

pivot_table은 pivot과 기능은 동일하지만, pivot와 달리 aggfunc를 지정할 수 있다.
중복값이 있을 경우 pivot은 사용할 수 없지만, pivot_table은 쓸 수 있다.

pivot_data.pivot_table(index='grade', columns='prod_cd', values='purch_amt')
# prod_cd	p1	 p2	   p3
# grade			
# A	      35.0	 12.5  15.0
# B	      0.0	 0.0   10.0
# pivot_table aggfunc 합계로 지정
pivot_data.pivot_table(index='grade', columns='prod_cd', values='purch_amt', aggfunc='sum')
# prod_cd	p1	p2	p3
# grade			
# A	        70	25	30
# B	        0	0	10

03) 인덱스 및 칼럼 라벨 변경하기
stack과 unstack

- stack: 칼럼 레벨에서 인덱스 레벨로 데이터프레임 변경 // 데이터를 행의 레벨로 쌓아올린다.
- unstack: 인덱스 레벨에서 칼럼 레벨로 위치를 변경하여 데이터를 쌓아 올린다.
# 데이터프레임 생성
stack_data = pd.DataFrame({'Location':['Seoul', 'Seoul', 'Seoul', 'kyoungggi', 'kyoungggi', 'Busan', 'Seoul', 'Seoul', 'Busan', 'kyoungggi', 'kyoungggi', 'kyoungggi'],
                           'Day':['Mon', 'Tue', 'Wed', 'Mon', 'Tue', 'Mon', 'Thu', 'Fri', 'Tue', 'Wed', 'Thu', 'Fri'],
                           'Rainfall':[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]})
print(stack_data.head(3))
#   Location	Day	Rainfall	Rainfall_precipitation	Temp
# 0	Seoul	    Mon	100	        80	                    32
# 1	Seoul	    Tue	80	        70	                    27
# 2	Seoul	    Wed	1000	    90	                    32

# Location과 Day를 인덱스로 설정하고 새로운 데이터프레임 생성
new_stack_data = stack_data.set_index(['Location', 'Day'])
print(new_stack_data)
# 	              Rainfall	Rainfall_precipitation	Temp
# Location	Day			
#     Seoul	Mon	      100	80	                    32
#           Tue	      80	70	                    27
#           Wed	      1000	90	                    32
# kyoungggi	Mon	      200	10	                    31
#           Tue	      200	20	                    30
#     Busan	Mon	      100	30	                    28
#     Seoul	Thu	      50	50	                    27
#           Fri	      100	90	                    25
#     Busan	Tue	      200	20	                    26
# kyoungggi	Wed	      100	80	                    33
#           Thu	      50	50	                    34
#           Fri	      100	10	                    31

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

new_stack_data.unstack(0)
#           Rainfall	                    Rainfall_precipitation	    Temp
# Location	Busan	Seoul	kyoungggi	Busan	Seoul	kyoungggi	Busan	Seoul	kyoungggi
# Day									
# Fri	    NaN	    100.0	100.0	    NaN	    90.0	10.0	    NaN	    25.0	31.0
# Mon	    100.0	100.0	200.0	    30.0	80.0	10.0	    28.0	32.0	31.0
# Thu	    NaN	    50.0	50.0	    NaN	    50.0	50.0	    NaN	    27.0	34.0
# Tue	    200.0	80.0	200.0	    20.0	70.0	20.0	    26.0	27.0	30.0
# Wed	    NaN	    1000.0	100.0	    NaN	    90.0	80.0	    NaN	    32.0	33.0

unstack(0)을 실행해보면, 첫 번째 인덱스 Location이 칼럼 레벨로 올라갔고 기존 칼럼인 'Rainfall', 'Rainfall_precipitation', 'Temp' 밑에 Location이 있으며 Location 별로 데이터를 확인할 수 있다.

new_stack_data.unstack(1)
# 	          Rainfall	                     Rainfall_precipitation	    Temp
#        Day  Fri   Mon   Thu   Tue   Wed	 Fri  Mon  Thu  Tue  Wed    Fri  Mon  Thu  Tue   Wed
#   Location															
#      Busan  NaN	100.0 NaN	200.0 NaN	 NaN  30.0 NaN	20.0 NaN	NaN	 28.0 NaN  26.0  NaN
#      Seoul  100.0	100.0 50.0	80.0  1000.0 90.0 80.0 50.0	70.0 90.0	25.0 32.0 27.0 27.0  32.0
#  kyoungggi  100.0	200.0 50.0	200.0 100.0	 10.0 10.0 50.0	20.0 80.0	31.0 31.0 34.0 30.0  33.0

unstack(1)을 실행해보면, 두 번째 인덱스였던 요일이 칼럼 레벨로 올라가있다.

stack은 칼럼 레벨에서 인덱스 레벨로 데이터프레임을 변경하는 것이다.

new_stack_data2 = new_stack_data.unstack(1)
new_stack_data2.stack(1, future_stack=True)
# 	            Rainfall	Rainfall_precipitation	Temp
# Location	Day			
#    Busan	Fri	    NaN	    NaN	                    NaN
#           Mon	    100.0	30.0	                28.0
#           Thu	    NaN	    NaN	                    NaN
#           Tue	    200.0	20.0	                26.0
#           Wed	    NaN	    NaN	                    NaN
#    Seoul	Fri	    100.0	90.0	                25.0
#           Mon	    100.0	80.0	                32.0

두 번째 칼럼 레벨에 있던 day가 다시 인덱스 레벨로 변경됐음을 확인할 수 있다.

Section 04 데이터프레임 병합하기

01) concat 활용하여 병합하기

concat()은 2개 이상의 데이터 프레임을 병합할 때 사용한다.

(1) 칼럼명이 같은 경우

df1 = pd.DataFrame({'col1':['사과', '배', '감', '수박', '멜론'],
                    'col2':[500, 1000, 2500, 5000, 3000]}, index=[0, 1, 2, 3, 4])
df2 = pd.DataFrame({'col1':['수박', '멜론', '딸기', '키위', '오렌지'],
                    'col2':[5000, 3000, 1000, 600, 700]}, index=[3, 4, 5, 6, 7])
pd.concat([df1, df2], ignore_index=False)
#   col1	col2
# 0	사과	500
# 1	배	    1000
# 2	감	    2500
# 3	수박	5000
# 4	멜론	3000
# 3	수박	5000
# 4	멜론	3000
# 5	딸기	1000
# 6	키위	600
# 7	오렌지	700

ignore_index=False 일 경우, 데이터프레임이 합쳐질 때 기존 인덱스가 유지된다.
True의 경우, 새 인덱스가 부여된다.

pd.concat([df1, df2], ignore_index=True)
#   col1	col2
# 0	사과	500
# 1	배	    1000
# 2	감	    2500
# 3	수박	5000
# 4	멜론	3000
# 5	수박	5000
# 6	멜론	3000
# 7	딸기	1000
# 8	키위	600
# 9	오렌지	700

axis를 지정하면 행 레벨로 지정할지 열 레벨로 병합할지 정의할 수 있다.

- axis=0 행 레벨로 병합 (위+아래(세로)로 합치기) // default
- axis=1 열 레벨로 병합(왼쪽+오른쪽(가로)으로 합치기)
pd.concat([df1, df2], axis=1)
#   col1	col2	col1	col2
# 0	사과	500.0	NaN	    NaN
# 1	배	   1000.0	NaN	    NaN
# 2	감	   2500.0	NaN	    NaN
# 3	수박   5000.0	수박	5000.0
# 4	멜론   3000.0	멜론	3000.0
# 5	NaN	      NaN	딸기	1000.0
# 6	NaN	      NaN	키위	600.0
# 7	NaN	      NaN	오렌지	700.0

칼럼명이 나란히 각각 쓰여 있고, 동일한 인덱스의 경우 나란히 합쳐지고 서로 다른 인덱스의 경우 NaN과 함께 각각 출력된다.

(2) 칼럼명이 다른 경우
concat()의 파라미터인 join으로 테이블을 병합할 수 있다.

join 값
- outer: 합집합 방식
- inner: 교집합 방식
# 데이터 프레임 생성
df3 = pd.DataFrame({'item':['item0', 'item1', 'item2', 'item3'],
                    'count':['count0', 'count1', 'count2', 'count3'],
                    'price':['price0', 'price1', 'price2', 'price3']}, index=[0,1,2,3])
df4 = pd.DataFrame({'item':['item2', 'item3', 'item4', 'item5'],
                    'count':['count2', 'count3', 'count4', 'count5'],
                    'price':['price2', 'price3', 'price4', 'price5'],
                    'var':['var2', 'var3', 'var4', 'var5']}, index=[2,3,4,5])

# join=outer
pd.concat([df3, df4], join='outer')
#    item	count	price	var
# 0	item0	count0	price0	NaN
# 1	item1	count1	price1	NaN
# 2	item2	count2	price2	NaN
# 3	item3	count3	price3	NaN
# 2	item2	count2	price2	var2
# 3	item3	count3	price3	var3
# 4	item4	count4	price4	var4
# 5	item5	count5	price5	var5
# join=inner
pd.concat([df3, df4], join='inner')
# 	item	count	price
# 0	item0	count0	price0
# 1	item1	count1	price1
# 2	item2	count2	price2
# 3	item3	count3	price3
# 2	item2	count2	price2
# 3	item3	count3	price3
# 4	item4	count4	price4
# 5	item5	count5	price5

(3) 인덱스가 중복인 경우
concat()의 verify_intergrity 속성을 사용한다.
True인 경우 중복 오류 메시지가 뜨고, False는 에러 메시지가 뜨지 않는다. False가 default이다.

df5 = pd.DataFrame({'A':['A0', 'A1', 'A3'], 'B':['B0', 'B1', 'B3'], 'C':['C0', 'C1', 'C3'], 'D':['D0', 'D1', 'D3']}, index=['I0','I1','I2'])
df6 = pd.DataFrame({'A':['AA2', 'A3', 'A4'], 'B':['BB2', 'B3', 'B4'], 'C':['CC2', 'C3', 'C4'], 'D':['DD2', 'D3', 'D4']}, index=['I2','I3','I4'])
pd.concat([df5, df6], verify_integrity=True) # ValueError: Indexes have overlapping values: Index(['I2'], dtype='object')

2) merge/join 활용하여 병합하기

merge와 join함수로 특정 key를 기준으로 데이터 프레임을 병합할 수 있다.

  • pd.merge 파라미터
    - on: 어떤 칼럼을 이용하여 merge할 건지 정의
    - how: merge 방식 정의
    # how 로 정의할 수 있는 merge 방식
    - inner: 일치하는 값이 있을 때만 가져온다. // default
    - left: 왼쪽 데이터프레임을 기준으로 오른쪽 데이터프레임을 병합(오른쪽 데이터프레임에 값이 없으면 NaN)
    - right: 오른쪽 데이터프레임을 기준으로 왼쪽 데이터프레임 병합
    - outer: left와 right를 합한 데이터프레임 병합
# customer_id 기준 병합
customer = pd.DataFrame({'customer_id':np.arange(6),
                         'name':['James', 'Elly', 'Tom', 'Givert', 'Aiden', 'Brody'],
                         '나이':[40, 20, 21, 30, 31, 18]})
orders = pd.DataFrame({'customer_id':[1, 1, 2, 2, 2, 3, 3, 1, 4, 9],
                       'item':['마우스', '충전기', '이어폰', '헤드셋', '전자펜', '키보드', '전자펜', '마우스', '키보드', '케이스'],
                       'quantity':[1, 2, 1, 1, 3, 2, 2, 3, 2, 1]})
                       
## how=inner
pd.merge(customer, orders, on='customer_id')
# = pd.merge(customer, orders, on='customer_id', how='inner')
# 	customer_id	name	나이	item	quantity
# 0	          1	Elly	20	   마우스	1
# 1	          1	Elly	20	   충전기	2
# 2	          1	Elly	20	   마우스	3
# 3	          2	Tom	    21	   이어폰	1
# 4	          2	Tom	    21	   헤드셋	1
# 5	          2	Tom	    21	   전자펜	3
# 6	          3	Givert	30	   키보드	2
# 7	          3	Givert	30	   전자펜	2
# 8	          4	Aiden	31	   키보드	2
# how=left
pd.merge(customer, orders, on='customer_id', how='left')
# 	customer_id	name	나이 item	quantity
# 0	          0	James	40	NaN	    NaN
# 1	          1	Elly	20	마우스	1.0
# 2	          1	Elly	20	충전기	2.0
# 3	          1	Elly	20	마우스	3.0
# 4	          2	Tom	    21	이어폰	1.0
# 5	          2	Tom	    21	헤드셋	1.0
# 6	          2	Tom	    21	전자펜	3.0
# 7	          3	Givert	30	키보드	2.0
# 8	          3	Givert	30	전자펜	2.0
# 9	          4	Aiden	31	키보드	2.0
# 10	      5	Brody	18	NaN	    NaN

왼쪽 데이터프레임(customer)에는 값이 있지만 오른쪽 프레임(orders)에 값이 없는 건 NaN으로 채워져 병합된다.
right는 반대로 오른쪽 프레임에 값이 있지만 왼쪽에 없는 값이 NaN으로 표시된다.

# how=outer
pd.merge(customer, orders, on='customer_id', how='outer')
# customer_id	name	나이	item	quantity
# 0	    0	   James	40.0	NaN	    NaN
# 1	    1	   Elly	    20.0	마우스	1.0
# 2	    1	   Elly	    20.0	충전기	2.0
# 3	    1	   Elly	    20.0	마우스	3.0
# 4	    2	   Tom	    21.0	이어폰	1.0
# 5	    2	   Tom	    21.0	헤드셋	1.0
# 6	    2	   Tom	    21.0	전자펜	3.0
# 7	    3	   Givert	30.0	키보드	2.0
# 8	    3	   Givert	30.0	전자펜	2.0
# 9	    4	   Aiden	31.0	키보드	2.0
# 10	5	   Brody	18.0	NaN	    NaN
# 11	9	   NaN	    NaN	    케이스	1.0

outer의 경우 'customer_id'를 모두 활용해 병합한 결과를 보여준다.

# 인덱스를 지정해 데이터프레임 합치기
cust1 = customer.set_index('customer_id')
order1 = orders.set_index('customer_id')
pd.merge(cust1, order1, left_index=True, right_index=True)
#               name	나이 item	quantity
# customer_id				
#           1	Elly	20	마우스	1
#           1	Elly	20	충전기	2
#           1	Elly	20	마우스	3
#           2	Tom	    21	이어폰	1
#           2	Tom	    21	헤드셋	1
#           2	Tom	    21	전자펜	3
#           3	Givert	30	키보드	2
#           3	Givert	30	전자펜	2
#           4	Aiden	31	키보드	2

customer_id를 인덱스로 설정한 두 데이터프레임을 merge하는 경우 on 파라미터를 사용하지 않고 'left_index'와 'right_index' 파라미터를 True로 지정해 두 인덱스 사이를 포함하는 'inner' 형태로 두 데이터프레임을 병합할 수 있다.

0개의 댓글