728x90
728x170
데이터 가공
- 분석하려는 원본 데이터는 사용자가 원하는 형태가 아니며, 활용하기 어려운 구조이기 때문에 전처리 과정이 필요하다.
- 데이터를 원하는 형태로 변형해 분석하기 쉽게 만드는 것은 빅데이터 분석의 궁극적인 목적, 즉 데이터 활용을 극대화하는 작업이며 시각화 전 단계로써 매우 중요한 과정이다.
- 판다스는 우리가 원하는 데이터 세트를 구성할 수 있도록 인덱스의 다양한 종류의 로직과 선형 대수 기능을 포함하는 시리즈와 데이터프레임을 쉽게 결합하는 여러 방법을 제공한다.
- 이 방법들을 이용해 서로 다른 데이터 세트를 가공하면 새로운 가치를 창출할 수 있다.
데이터 이어 붙이기
- 판다스의 concat() 함수를 이용하면 시리즈와 데이터프레임을 이어 붙일 수 있다.
- 특히 같은 길이의 행이나 열을 따라 데이터를 이어 붙일 때 이 함수를 주로 적용한다.
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2'], 'C': ['C0', 'C1', 'C2']}, index=[0, 1, 2])
df2 = pd.DataFrame({'A': ['A3', 'A4', 'A5'], 'B': ['B3', 'B4', 'B5'], 'C': ['C3', 'C4', 'C5']}, index=[3, 4, 5])
df3 = pd.DataFrame({'A': ['A6', 'A7', 'A8'], 'B': ['B6', 'B7', 'B8'], 'C': ['C6', 'C7', 'C8']}, index=[6, 7, 8])
- pandas.concat()은 numpy.concatenate() 와 같이 매개 변수로 리스트 또는 같은 타입인 객체들의 딕셔너리를 가진다.
frames = [df1, df2, df3]
result = pd.concat(frames)
result1 = pd.concat(frames, keys=['x', 'y', 'z'])
result
result1
- DataFrame 객체 result1의 인덱스는 멀티 인덱스이다.
- 이는 키를 이용해 다음과 같이 해당하는 데이터를 선택할 수 있다는 의미이다.
result1.loc['z']
축의 로직 설정과 append를 사용하여 이어 붙이기
- 여러 데이터프레임을 이용해 연산할 때 이어 붙이는 축은 제외하고 다른 축을 처리하는 방법은 다음과 같다.
- join='outer' 로 합집합(Union)을 취한다.
- join-'inner' 로 교집합(Intersection)을 취한다.
- 생성된 객체 df1과 df4를 축 열을 기준으로 이어 붙이면 join='outer'를 사용한 것과 같은 결과가 나타난다.
df4 = pd.DataFrame({'B': ['B2', 'B6', 'B7'],
'C': ['C2', 'C6', 'C7'],
'E': ['E2', 'E6', 'E7']}, index=[2, 6, 7])
result = pd.concat([df1, df4], axis=1, sort=False)
result
- join='inner'를 입력하면 행 인덱스가 겹치는 부분만 나타낸다.
result = pd.concat([df1, df4], axis=1, join='inner')
result
- 다음은 df1의 index를 기준으로 결합한 결과이다.
result = pd.concat([df1, df4], axis=1).reindex(df1.index)
result
- 데이터프레임 객체들 사이에 인덱스가 중복되는 경우, ignore_index 인수를 사용하여 무시할 수 있다.
result = pd.concat([df1, df4], ignore_index=True)
result
- 시리즈와 데이터프레임에 append() 메소드를 사용하면 concat()을 사용한 것과 같은 결과를 얻을 수 있다.
result = df1.append(df2)
result
- append() 메소드를 사용한 결과 df1과 df4의 인덱스가 중복되더라도 개별 순서대로 표시된다.
- 통합된 열 라벨에 해당하는 요솟값이 없을 때는 손실 값으로 처리되어 표시된다.
result = df1.append(df4, sort=False)
result
- append() 로 시리즈나 딕셔너리를 전달하면 1개 행을 데이터프레임에 이어 붙일 수 있다.
s1 = pd.Series(['Q0', 'Q1', 'Q2', 'Q3'], index=['A', 'B', 'C', 'D'])
result = df1.append(s1, ignore_index=True)
result
- 데이터프레임과 딕셔너리 사이에 append( )를 적용할 때, ignore_index 인수를 이용해 인덱스를 차례대로 표기할 것인지 두 객체를 독립적으로 표기할지 정할 수 있다.
dicts = [{'A': 1, 'B': 2, 'X': 3}, {'A': 4, 'B': 5, 'Y': 6}]
result = df1.append(dicts, ignore_index=True, sort=False)
result
차원이 다른 시리즈와 데이터프레임 이어 붙이기
- 시리즈와 데이터프레임 객체를 이어 붙일 수 있다.
- 이때 시리즈의 이름을 이어 붙이는 객체의 열 이름으로 바꾸어 처리한다.
s2 = pd.Series(['Z0', 'Z1', 'Z2', 'Z3'], name='Z')
result = pd.concat([df1, s2], axis=1)
result
- 데이터프레임을 이름이 없는 시리즈와 이어 붙이면 열 이름을 연속적인 숫자로 표시한다.
s3 = pd.Series(['*0', '*1', '*2'])
result = pd.concat([df1, s3, s3, s3], axis=1)
result
그룹 키로 이어 붙이기
- 그룹 키로 데이터를 이어 붙이는 방법을 알아보기 위해 먼저 시리즈 객체 3개를 결합해 하나의 프레임을 만든다.
s4 = pd.Series([0, 1, 2, 3], name='J')
s5 = pd.Series([0, 1, 2, 3])
s6 = pd.Series([0, 1, 4, 5])
pd.concat([s4, s5, s6], axis=1)
- keys 인수를 이용해 현재 열 이름을 수정할 수 있다.
pd.concat([s4, s5, s6], axis=1, keys=['ha', 'hi', 'ho'])
- 다음 예제에서는 키를 적용해 frames를 구성하는 df1, df2, df3을 이어 붙인다.
- result1과 같이 concat() 안에 딕셔너리를 전달한 결과는 result 결과와 동일하다.
result = pd.concat(frames, keys=['ha', 'hi', 'ho'])
pic = {'ha': df1, 'hi': df2, 'ho': df3}
result1 = pd.concat(pic)
result1
- keys 인수로 순서를 변경하거나 이 인수를 부분적으로 적용해 데이터를 이어 붙일 수 있다.
result = pd.concat(pic, keys=['ho', 'hi'])
result
데이터베이스 타입의 데이터프레임 또는 시리즈를 합치기
- 데이터프레임은 데이터의 행이나 열이 테이블 형태로 정렬되는 2차원 데이터 구조이다.
- 시리즈는 행이나 열로 정렬되므로, 데이터프레임과 연산할 수 있다.
- 이러한 객체들을 merge(), join(), concat() 과 같은 함수를 이용해 합치거나 붙일 수 있다.
merge() 함수로 합치기
- merge() 함수의 인수 on, how를 사용해 데이터를 합치는 연산을 할 때, inner, left, outer, right, full 값을 활용할 수 있다.
- merge() 함수를 이용해 데이터를 합치는 방법을 살펴보기 위해 먼저 다음과 같이 연산 대상 객체 df1과 df2를 생성한다.
df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})
df2 = pd.DataFrame({'key': ['B', 'D', 'D', 'E'], 'value': np.random.randn(4)})
- 앞에서 생성한 df1, df2 객체와 같은 이름과 구조를 가진 2개의 데이터베이스 테이블이 있다고 가정한다.
- 그리고 4개의 연산 타입 중, INNER JOIN을 실행한다.
- INNER JOIN은 인수 how의 기본값이므로 how는 생략할 수 있다.
pd.merge(df1, df2, on='key')
더보기
- 다음의 SQL문과 같다.
SELECT *
FROM df1
INNER JOIN df2
ON df1.key = df2.key;
- 두 데이터프레임 객체에 LEFT OUTER JOIN 연산을 실행하는 SQL 구문과 판다스 연산의 결과는 다음과 같다.
pd.merge(df1, df2, on='key', how='left')
더보기
- 다음의 SQL문과 같다.
SELECT *
FROM df1
LEFT OUTER JOIN df2
ON df1.key = df2.key;
- 두 데이터프레임 객체에 RIGHT OUTER JOIN 연산을 실행하는 SQL 구문과 판다스 연산의 결과는 다음과 같다.
pd.merge(df1, df2, on='key', how='right')
더보기
- 다음의 SQL문과 같다.
SELECT *
FROM df1
RIGHT OUTER JOIN df2
ON df1.key = df2.key;
- 판다스는 두 객체를 모두 나타내는 FULL JOIN 연산을 허용한다.
- 이 연산은 MySQL 같은 모든 관계형 데이터베이스 관리 시스템(Relational Database Management System, RDMS)에서 지원하지는 않는다.
pd.merge(df1, df2, on='key', how='outer')
더보기
- 다음의 SQL문과 같다.
SELECT *
FROM df1
FULL OUTER JOIN df2
ON df1.key = df2.key;
- 다음은 key1, key2를 기준으로 데이터프레임을 합치는 예제이다.
- merge() 함수의 인수 how='inner'가 기본값이므로, 인수 on에 key1, key2를 입력하여 교집합으로 연산한다.
- 데이터프레임 left와 right를 결합하는 merge()의 인수 on에서 key1과 key2의 교집합 범위로 연산한 결과가 result이다.
left = pd.DataFrame({'key1': ['Z0', 'Z0', 'Z1', 'Z2'], 'key2': ['Z0', 'Z1', 'Z0', 'Z1'],
'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['Z0', 'Z1', 'Z1', 'Z2'], 'key2': ['Z0', 'Z0', 'Z0', 'Z0'],
'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']})
result = pd.merge(left, right, on=['key1', 'key2'])
result
- merge() 함수에 how 인수를 적용하면 결과 테이블에 어떤 키를 포함할지 결정할 수 있다.
- key로 조합할 때 왼쪽이나 오른쪽 중 해당 요솟값이 없으면 결과 테이블에서 NA로 나타난다.
- merge() 함수에서 사용하는 how 옵션과 SQL에서 사용하는 JOIN Name의 내용은 다음과 같다.
how 옵션 | SQL 조인(JOIN) 문 | 내용 |
left | LEFT OUTER JOIN | 왼쪽 프레임의 키들을 사용 |
right | RIGHT OUTER JOIN | 오른쪽 프레임의 키들을 사용 |
outer | FULL OUTER JOIN | 양쪽 프레임 키들의 합집합 사용 |
inner | INNER JOIN | 양쪽 프레임 키들의 교집합 사용 |
- how='left'인 LEFT OUTER JOIN은 다음과 같이 실행한다.
- result는 left 객체의 key1과 key2를 기준으로 표시된다.
result = pd.merge(left, right, how='left', on=['key1', 'key2'])
result
- how='right'인 RIGHT OUTER JOIN은 다음과 같이 실행한다.
- result는 right 객체의 key1과 key2를 기준으로 표시된다.
result = pd.merge(left, right, how='right', on=['key1', 'key2'])
result
- how='outer'인 FULL OUTER JOIN은 다음과 같이 실행한다.
- result는 left와 right 객체의 key1과 key2의 합집합 범위를 기준으로 표시된다.
result = pd.merge(left, right, how='outer', on=['key1', 'key2'])
result
JOIN() 메소드로 합치기
- DataFrame.join() 메소드를 사용하면 2개의 다른 인덱스를 갖는 데이터프레임을 하나의 데이터프레임으로 편리하게 결합할 수 있다.
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']}, index=['Z0', 'Z1', 'Z2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['Z0', 'Z2', 'Z3'])
result = left.join(right)
result
- 다음은 합집합 범위인 인수 how='outer'로 연산한 예제이다.
result = left.join(right, how='outer')
result
- 다음은 교집합 범위인 인수 how='inner'로 연산한 예제이다.
result = left.join(right, how='inner')
result
- join() 메소드는 열 또는 여러 열의 이름을 입력할 수 있는 on 인수를 옵션으로 갖는다.
- 이 인수를 데이터프레임에 전달하면 on에 입력된 열을 기준으로 정렬한다.
left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'], 'key': ['Z0', 'Z1', 'Z0', 'Z1']})
right = pd.DataFrame({'C': ['C0', 'C1'], 'D': ['D0', 'D1']}, index=['Z0', 'Z1'])
result = left.join(right, on='key')
result
멀티 인덱스 객체 합치기
- 다음은 데이터프레임 left와 열 라벨인 key1과 key2를 리스트로 묶은 인수 on을 join() 메소드에 적용하여 데이터프레임 right 와 결합하는 예제이다.
- left의 key1, key2의 요솟값과 같은 이름을 가지는 데이터프레임 right의 멀티 인덱스 라벨에 해당하는 요솟값을 결합할 때 join()의 인수 on에 전달한 left의 열 라벨을 기준으로 결합한다.
left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'],
'key1': ['Z0', 'Z0', 'Z1', 'Z2'], 'key2': ['Z0', 'Z1', 'Z0', 'Z1']})
ind = pd.MultiIndex.from_tuples([('Z0', 'Z0'), ('Z1', 'Z0'), ('Z2', 'Z0'), ('Z2', 'Z1')])
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']}, index=ind)
result = left.join(right, on=['key1', 'key2'])
result
- 멀티 인덱스를 가지는 데이터프레임을 하나의 인덱스를 가지는 데이터프레임과 합칠 수 있다.
- 이때 레벨은 하나의 인덱스를 가지는 데이터프레임의 인덱스 이름을 기준으로 결정된다.
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']},
index=pd.Index(['Z0', 'Z1', 'Z2'], name='key'))
ind = pd.MultiIndex.from_tuples([('Z0', 'Y0'), ('Z1', 'Y1'), ('Z2', 'Y2'), ('Z2', 'Y3')],
names=['key', 'Y'])
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3']}, index=ind)
result = left.join(right, how='inner')
result
- merge() 함수를 이용한 다음 예제의 결과는 위의 result와 같다.
result = pd.merge(left.reset_index(), right.reset_index(), on=['key'], how='inner').set_index(['key', 'Y'])
- 멀티 인덱스를 가지는 left와 right를 join() 메소드를 이용해 다음과 같이 결합한다.
l_ind = pd.MultiIndex.from_product([list('abc'), list('xy'),
[1, 2]], names=['abc', 'xy', 'num'])
left = pd.DataFrame({'z1': range(12)}, index=l_ind)
r_ind = pd.MultiIndex.from_product([list('abc'), list('xy')], names=['abc', 'xy'])
right = pd.DataFrame({'z2': [100 * i for i in range(1, 7)]}, index=r_ind)
left
right
- join() 메소드에 on=['abe', 'xy'], how='inner' 인수를 전달해서 left 객체에 right 객체를 결합한다.
left.join(right, on=['abc', 'xy'], how='inner')
멀티 인덱스 레벨을 조합해 합치기
- 다음은 merge() 메소드의 인수 on에 인덱스 레벨과 열 조합으로 구성된 key1과 key2의 리스트를 적용하는 경우이다.
- 이렇게 두 데이터프레임을 결합할 때는 이 프레임들의 key1과 key2의 요소가 같아야만 연산할 수 있다.
l_ind = pd.Index(['Z0', 'Z0', 'Z1', 'Z2'], name='key1')
left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'], 'B': ['B0', 'B1', 'B2', 'B3'], 'key2': ['Z0', 'Z1', 'Z0', 'Z1']}, index=l_ind)
r_ind = pd.Index(['Z0', 'Z1', 'Z2', 'Z2'], name='key1')
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'], 'D': ['D0', 'D1', 'D2', 'D3'], 'key2': ['Z0', 'Z0', 'Z0', 'Z1']}, index=r_ind)
result = left.merge(right, on=['key1', 'key2'])
result
중복되는 열 처리하기
- merge() 함수의 연산 결과에서 중복되는 열을 명확히 구분하기 위해 다음과 같이 접미사(suffixes)를 붙일 수 있다.
left = pd.DataFrame({'z': ['Z0', 'Z1', 'Z2'], 'v': [1, 2, 3]})
right = pd.DataFrame({'z': ['Z0', 'Z0', 'Z3'], 'v': [4, 5, 6]})
result = pd.merge(left, right, on='z')
result1 = pd.merge(left, right, on='z', suffixes=['_l', '_r'])
result
result1
- DataFrame.join()에 lsuffix와 rsuffix 인수를 적용하면 왼쪽 열 이름에 l 접미사를, 오른쪽 열 이름에 r 접미사를 붙일 수 있다.
left = left.set_index('v')
right = right.set_index('v')
result = left.join(right, lsuffix='_l', rsuffix='_r')
result
시리즈나 데이터프레임의 열 안에서 값을 합치기
- 다음은 combine_first() 메소드를 이용해 시리즈나 데이터프레임 객체의 인덱스를 다른 객체의 인덱스와 매칭하면서 손실 값들을 수정(Patch)하는 예제이다.
df1 = pd.DataFrame([[np.nan, 3., 5.], [-4.6, np.nan, np.nan], [np.nan, 7., np.nan]])
df2 = pd.DataFrame([[-2.6, np.nan, -8.2], [-5., 1.6, 4]], index=[1, 2])
result = df1.combine_first(df2)
result1 = df2.combine_first(df1)
result
result1
- 다음 예제에서 update() 메소드는 df1을 df2로 업데이트하며, 위의 df2.combine_first(df1)과 결과가 같다.
df1.update(df2)
df1
데이터 재형성하기
- 빅데이터를 분석하려면 수집한 원본 데이터를 분석하기 쉽도록 원하는 형태로 변경해야 한다.
- 판다스에서 제공하는 재형성(Reshaping) 도구들은 데이터를 변경할 수 있게 한다.
- 테이블, 데이터프레임, 시리즈와 구조를 변경하는 이 도구들은 빅데이터 관련 애플리케이션 중 판다스에서만 제공하는 독특한 기능이다.
데이터프레임 객체 피벗
- 피벗(Pivot) : 회전 또는 균형을 맞추는 중심축이나 고정점
- 데이터 처리에서 피벗 테이블
- 데이터베이스, 액셀 같은 스프레드시트, 인공지능 개발 프로그램 등에서 사용하는 더 확장적인 테이블
- 행, 열 값의 데이터들을 요약하는 통계분야에서 중요하게 사용된다.
- 데이터를 유용한 정보 형태로 변형시키기 위해 통계를 배열하고, 재배열하기 쉽게 한다.
- 데이터프레임 객체를 피벗하는 방법을 살펴보기 위해 다음의 데이터프레임 객체를 생성한다.
data = {'name': ['haena', 'naeun', 'una', 'bum', 'suho'], 'type': ['tennis', 'tennis', 'swim', 'swim', 'tennis'],
'records': ['A', 'B', 'C', 'A', 'B'], 'sex': ['F', 'F', 'F', 'M', 'M'], 'period': [3, 3, 1, 5, 2]}
df = pd.DataFrame(data)
df
- 데이터프레임의 pivot() 메소드에 index와 columns 인수를 전달하면 재형성된 데이터프레임 객체를 결과로 반환한다.
- pivot() 메소드는 index, columns, values 인수를 가진다.
- df 객체의 테이블을 학생들이 활동하는 소프츠 종목에 따라 기록과 성별로 구분하여 알기 쉽게 재형성 또는 피벗할 수 있다.
- 다음 예제에서는 이름을 인덱스로 하고, 참가하는 스포츠 종목을 열로 배치한 다음 이에 따른 기록과 성별에 해당하는 값들을 재배치하여 일목요연하게 확인할 수 있게한다.
- 기록인 records 값 관련 내용만 확인하고 싶다면 values='records' 인수를 설정한다.
- 이때 매칭되지 않는 요소는 NaN으로 채워진다.
dfp = df.pivot(index='name', columns='type', values=['records', 'sex'])
dfp
피벗 테이블
- pivot() 메소드를 사용하면 문자열, 수치 등 여러 데이터 타입을 피벗할 수 있다.
- 판다스에서는 수치 데이터를 결합하는 pivot_table() 메소드를 제공한다.
- pivot_table() 메소드는 스프레드시트 스타일의 피벗 테이블을 생성하는 데 사용한다.
dfp = df.pivot_table(index='type', columns='records', values='period', aggfunc=np.max)
dfp
더보기
- np.max 대신에 필요에 따라 np.min, np.mean 등으로 대체할 수 있다.
- pivot_table() 메소드를 실행하기 위해 먼저 다음과 같이 데이터프레임 객체를 생성한다.
import datetime
df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 6, 'B': ['x', 'y', 'w'] * 8,
'C': ['ha', 'ha', 'ha', 'hi', 'hi', 'hi'] * 4, 'D': np.arange(24),
'E': [datetime.datetime(2021, i, 1) for i in range(1, 13)] + [datetime.datetime(2021, i, 15) for i in range(1, 13)]})
df
- 위의 데이터에서 다음과 같이 피벗 테이블을 생성한다.
pd.pivot_table(df, values='D', index=['A', 'B'], columns='C')
- aggfunc=np.sum 인수를 전달하고 다른 매개 변수를 설정하여 객체를 재형성한다.
- 테이블 생성 시 중복되는 값은 서로 더한다.
- 위의 데이터에서 다음과 같이 피벗 테이블을 생성한다.
pd.pivot_table(df, values='D', index=['B'], columns=['A', 'C'], aggfunc=np.sum)
- 손실값인 NaN을 지우고 공간으로 나타내고 싶다면 데이터프레임의 to_string() 메소드에 na_rep 인수를 전달한다.
df_pt = pd.pivot_table(df, values='D', index=['B'], columns=['A', 'C'], aggfunc=np.sum)
str_df = df_pt.to_string(na_rep='')
print(str_df)
교차표
- 교차표(Cross Tabulation) : 분석을 수월하게 할 수 있도록 2개 이상의 범주를 단순하게 교차하여 그룹화하는, 표 형태로 작성한 데이터
- 판다스의 crosstab() 함수를 사용하면 교차표를 생성하고 데이터프레임을 반환한다.
- index와 columns에는 유사 배열, 시리즈 또는 배열의 리스트나 시리즈의 리스트를 입력한다.
- values 옵션에는 유사 배열을 전달한다.
- values 옵션 배열과 aggfunc를 전달하지 않으면 기본으로 테이블의 도수를 계산한다.
- 다음 예제에서는 그룹별 데이터의 도수를 나타내는 교차표를 생성한다.
- 행 라벨과 열 라벨을 표시하려면 rownames와 colnames 인수를 사용한다.
- 인수를 명시하지 않으면 행과 열에 자동으로 라벨을 부여한다.
ha, hi, top, down, one, two = 'ha', 'hi', 'top', 'down', 'one', 'two'
a = np.array([ha, ha, hi, hi, ha, ha], dtype=object)
b = np.array([one, one, two, one, two, one], dtype=object)
c = np.array([top, top, down, top, top, down], dtype=object)
pd.crosstab(a, [b, c], rownames=['a'], colnames=['b', 'c'])
- crosstab() 함수에 2개의 시리즈를 전달하면 도수 테이블을 생성한다.
df = pd.DataFrame({'A': [1, 2, 2, 2, 2], 'B': [3, 3, 7, 7, 7], 'C': [1, 1, np.nan, 1, 1]})
df
pd.crosstab(df.A, df.B)
- crosstab() 함수에 normalize 옵션을 사용해 백분율 교차표를 생성할 수 있다.
pd.crosstab(df.A, df.B, normalize=True)
- normalize 옵션을 사용하면 각 행이나 열 내에서 값들을 정규화(Normalization) 할 수 있다.
- 일반적으로 서로 다른 데이터 세트에 동일한 스케일로 최솟값 0, 최댓값 1을 적용해 0과 1 사이의 값으로 변환된 값을 비교하거나 영향도를 평가하기 위해 정규화를 사용한다.
pd.crosstab(df.A, df.B, normalize='columns')
- 다음 예제에서 crosstab() 함수의 첫 번째 2개시리즈인 df.A와 df.B가 교차할 때 해당 요소의 값인 values는 C열의 요솟값이다. aggfunc=np.sum을 적용했으므로 df.A와 df.B가 교차하는 C열의 요솟값이 같으면 서로 더한다.
pd.crosstab(df.A, df.B, values=df.C, aggfunc=np.sum)
- 다음으로 margins 인수를 추가하고 출력을 정규화한다.
- normalize=True 를 인수로 취한 NaN은 0.0으로 취급한다.
- 여기에 margins=True 인수를 추가하면 행과 열의 모든 요솟값을 더한 결과가 나타난다.
pd.crosstab(df.A, df.B, values=df.C, aggfunc=np.sum, normalize=True, margins=True)
더미 변수 계산
- 더미(Dummy) 변수는 지표(Indicator) 변수라고도 불리며, 통계학과 계량 경제학의 회기 분석에서 사용하는 용어이다.
- 이는 영역별로 효과 유무를 나타내기 위해 0이나 1을 취하는 변수를 의미하며, 이를 위해 get_dummies() 함수를 사용한다.
- 예를 들어 남, 여와 같은 범주형 변수를 데이터프레임의 더미 변수로 반환해 범주형 변수를 연속형 범주처럼 정량화하고 비교할 수 있게 한다.
df = pd.DataFrame({'key': list('bbacab'), 'data1': range(6)})
pd.get_dummies(df['key'])
- 다음 예제에서는 df 객체의 열 이름에 접두사 key를 붙이는 dummies 객체를 생성한다.
- 그리고 df 객체에서 열 이름이 data1인 객체를 생성한 후 dummies 객체를 덧붙인다.
- df['data1']은 시리즈이고, df[['data1]]은 데이터프레임이라는 점에 주의한다.
- 여기에 적용한 join()은 데이터프레임의 메소드이다.
dummies = pd.get_dummies(df['key'], prefix='key')
dummies
df[['data1']].join(dummies)
- get_dummies() 함수는 종종 cut()과 같은 이산 함수와 함께 사용한다.
val = np.random.randn(7)
val
더보기
array([-0.19752499, -0.10805769, 0.0837273 , 1.92189691, -1.40128409,
0.13794623, -0.31155545])
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
pd.get_dummies(pd.cut(val, bins))
stack()과 unstack() 메소드로 재형성
- 시리즈와 데이터프레임에 적용하는 stack()과 unstack() 메소드는 pivot() 메소드와 밀접하게 연관된다.
- 이들 메소드는 MultiIndex 객체와 함께 작용하도록 설계되었다.
- stack() 메소드는 열 라벨을 가장 안쪽의 인덱스로 지정한 레벨을 쌓는다.
- unstack() 메소드는 가장 안쪽의 인덱스 레벨을 새로운 열 라벨로 가지는 데이터프레임을 반환한다.
- stack() 과 unstack() 메소드를 이해하기 위해 멀티 인덱스를 가진 객체를 다음과 같이 생성한다.
tup = list(zip(*[['ha', 'ha', 'hi', 'hi', 'ho', 'ho', 'hu', 'hu'],
['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]))
ind = pd.MultiIndex.from_tuples(tup, names=['1st', '2nd'])
df = pd.DataFrame(np.random.randn(8, 2), index=ind, columns=['A', 'B'])
df1 = df[:4]
df1
- df1에 stack()을 실행하면 df1의 열 라벨이 멀티 인덱스에서 가장 안쪽에 위치하는 가장 낮은 레벨이 된다.
>>> stacked = df1.stack()
>>> stacked
1st 2nd
ha one A 1.033304
B 0.059639
two A 0.156576
B 0.012747
hi one A -0.247870
B 0.058336
two A 1.424776
B 0.437172
dtype: float64
>>> type(stacked)
pandas.core.series.Series
- 데이터프레임이나 시리즈에 stack() 메소드를 실행한 결과의 인덱스가 멀티인덱스일 때 stack()의 역연산인 unstack()을 실행하면 기본으로 A, B가 열의 라벨이 된다.
stacked.unstack()
- 다음은 unstack() 메소드에 숫자 1 또는 이름이 2nd인 인덱스 레벨 인수로 전달해 두 번째 인덱스 라벨이 열이 되도록 연산하는 예제이다.
stacked.unstack(1)
- 다음은 unstack() 메소드에 숫자 0 또는 이름인 1st를 인덱스 레벨 인수를 전달해 연산하는 예제이다.
stacked.unstack('1st')
- stack() 과 unstack() 메소드는 포함된 인덱스 레벨의 순서를 내부적으로 정렬한다.
- 따라서 df와 df를 unstack, stack 연산한 결과의 순서는 같지 않다.
ind = pd.MultiIndex.from_product([[2, 1], ['a', 'b']])
df = pd.DataFrame(np.random.randn(4), index=ind, columns=['top'])
df
df.unstack().stack()
>>> all(df.unstack().stack() == df.sort_index())
True
>>> all(df.unstack().stack() == df)
ValueError: Can only compare identically-labeled DataFrame objects
melt() 메소드로 재형성
- pandas.melt() 함수와 DataFrame.melt() 메소드는 1개 이상 열들을 식별자 변수 형식으로 재형성할 때 유용하다.
- 매개 변수 var_name과 var_name을 이용해 열 이름을 다음과 같이 설정할 수 있다.
df = pd.DataFrame({'first': ['Haena', 'Suho'], 'last': ['Kang', 'Chae'], 'age': [30, 18], 'score': [100, 85]})
df
df.melt(id_vars=['first', 'last'])
df.melt(id_vars=['first', 'last'], var_name='personal')
파이썬 정규 표현식 사용하기
- REs, regexes 또는 regex pattern 이라 불리는 정규 표현식(Regular Expression)은 문자열 집합에 정규 표현식 규칙을 적용해 원하는 문자열의 집합으로 나타나도록 연산한다.
- 정규 표현식은 파이썬에 내장된 특수한 프로그래밍 언어로써, 소프트웨어 패키지인 re 모듈을 임포트해 사용할 수 있다.
- 간단히 말하자면, 정규 표현식은 찾기 패턴을 정의하는 문자들의 시퀀스이며, 패턴은 문자열을 찾는 알고리즘이다.
- 명시하는 찾기 패턴이 문자열에 포함되는지 확인한다.
- 정규 표현식은 코딩 스크립트를 줄여 경제적이고 간략하게 만들기 위해 사용한다.
정규 표현식 구문
- 자세한 내용은 이곳(https://www.w3schools.com/python/python_regex.asp)을 참고한다.
텍스트 데이터 가공하기
- 텍스트 데이터는 해석이 까다롭고 복잡하여 많은 문자와 기호 등을 포함하고 있어 원하는 형태로 가공하기 어렵다.
- pandas.Series와 pandas.Index 클래스에는 배열 요소를 쉽게 연산하도록 하는 str 메소드가 있어 텍스트 데이터를 가공할 수 있다.
- str 메소드는 pandas.core.strings.StringMethods의 별칭으로써, str로 간단히 사용한다.
- str 메소드에는 다양한 메소드가 있다.
- 특히 정규 표현식을 사용하고자 할 때 정규 표현식 모듈인 re를 임포트해 적용하는 것 외에도 StringMethods의 메소드 패턴을 적용할 수 있다.
메소드 | 기능 |
lower() | 배열의 문자열을 소문자로 변경 |
upper() | 배열의 문자열을 대문자로 변경 |
len() | 배열의 각 문자열의 길이를 계산 |
strip() | 배열의 각 문자열로부터 공백(줄 바꿈 포함)을 제거 |
lstrip() | 배열의 각 문자열의 왼쪽부터 공백을 제거 |
rstrip() | 배열의 각 문자열의 오른쪽부터 공백을 제거 |
replace() | Series/Index에서 pattern/regex가 적용된 것으로 대체 |
split() | 주어진 정규 표현식의 패턴이나 구분자로 각 문자열을 분할 |
cat() | 주어진 구분자로 문자열 배열을 이어 붙인다. |
contains() | 각 문자열이 주어진 정규 표현식 패턴을 포함하는지를 불리언 배열로 반환 |
count() | 각 문자열에서 패턴이 일치하는 개수를 계산 |
findall() | 패턴이나 정규 표현식이 발생하는 모든 경우를 구한다. |
get() | 배열 요소에 있는 리스트, 튜플 또는 문자열에서 요소를 추출 |
extract() | 전달한 정규 표현식을 사용하여 각 문자열에서 그룹들을 찾는다. |
extractall() | 정규 표현식의 패턴에 모두 매칭하는 그룹들을 찾는다. |
endswith() | 각 문자열이 전달한 패턴으로 끝나는지를 나타내는 불리언 배열을 반환한다. |
startswith() | 각 문자열이 전달한 패턴으로 시작하는지를 나타내는 불리언 배열을 반환한다. |
- str 메소드는 호출할 수 없는 메소드이므로 소괄호를 생략한다.
>>> ser = pd.Series(['Suho', 'AA', np.nan, 'rabbit'])
>>> type(ser.str)
pandas.core.strings.StringMethods
>>> ser.str.lower()
ser.str.lower()
ser.str.lower()
0 suho
1 aa
2 NaN
3 rabbit
dtype: object
>>> ser.str.upper()
0 SUHO
1 AA
2 NaN
3 RABBIT
dtype: object
>>> ser.str.len()
0 4.0
1 2.0
2 NaN
3 6.0
dtype: float64
- 다음은 텍스트에 있는 공백을 제거하기 위해 str 메소드를 적용하는 예제이다.
>>> ind = pd.Index([' ha', 'hi ', ' ho ', 'hu'])
>>> ind.str.strip()
Index(['ha', 'hi', 'ho', 'hu'], dtype='object')
>>> ind.str.lstrip()
Index(['ha', 'hi ', 'ho ', 'hu'], dtype='object')
>>> ind.str.rstrip()
ind.str.rstrip()
ind.str.rstrip()
Index([' ha', 'hi', ' ho', 'hu'], dtype='object')
- Index 클래스에도 str 메소드를 적용할 수 있어 데이터프레임 열을 정리하거나 변환하는 데 유용하다.
df = pd.DataFrame( np.random.randn(2, 2), columns=[' Column A ', ' Column B '],
index=range(2))
df
>>> df.columns
Index([' Column A ', ' Column B '], dtype='object')
- 예제의 df.columns 객체는 Index 객체이므로 str 메소드를 사용할 수 있다.
>>> df.columns.str.strip()
Index(['Column A', 'Column B'], dtype='object')
>>> df.columns.str.lower()
Index([' column a ', ' column b '], dtype='object')
- 다음 예제에서는 str 메소드를 이용해 열 텍스트 양쪽 끝에 있는 공백을 제거한다.
- 또한 열 라벨을 소문자로 변경하고 나머지 공간을 언더 바(_)로 대체한 결과를 열에 동적 할당한다.
df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_')
df
문자열을 분할하고 대체하기
- split() 메소드는 리스트의 시리즈를 반환한다.
>>> ser1 = pd.Series(['ha_a_b', 'hi_c_d', np.nan, 'ho_e_f'])
>>> ser1.str.split('_')
0 [ha, a, b]
1 [hi, c, d]
2 NaN
3 [ho, e, f]
dtype: object
- get() 메소드나 [] 표기법을 사용하면 분할한 리스트에 있는 요소에 접근할 수 있다.
>>> ser1.str.split('_').str.get(1)
0 a
1 c
2 NaN
3 e
dtype: object
>>> ser1.str.split('_').str[1]
0 a
1 c
2 NaN
3 e
dtype: object
- 다음 예제에서는 결과로 데이터프레임을 얻기 위해 인수 expand=True를 전달한다.
- 예제의 expand 인수는 시리즈를 데이터프레임으로 확장한다.
ser1.str.split('_', expand=True)
- replace() 메소드는 기본으로 정규 표현식을 대체한다.
>>> ser = pd.Series(['Suho', 'bAAa', np.nan, 'cute_dog'])
>>> ser
0 Suho
1 bAAa
2 NaN
3 cute_dog
dtype: object
>>> ser.str.replace('^.a|dog', '***', case=False)
0 Suho
1 ***Aa
2 NaN
3 cute_***
dtype: object
- replace() 메소드는 re.compile() 패턴에서 컴파일 처리된 정규 표현식을 수용한다.
- 이때 모든 flags는 컴파일된 정규 표현식 객체에 포함되어야 한다.
>>> import re
>>> pattern = re.compile('^.a|dog', flags=re.IGNORECASE)
>>> ser.str.replace(pattern, '***')
0 Suho
1 ***Aa
2 NaN
3 cute_***
dtype: object
텍스트 이어 붙이기
- cat() 메소드를 사용해 시리즈나 인덱스를 이어 붙일 수 있다.
- 기본값은 인수 sep='' 이다.
- 단일 시리즈를 문자열로 이어 붙이는 예제는 다음과 같다.
>>> ser = pd.Series(['ha', 'hi', 'ho'])
>>> ser.str.cat(sep=',')
'ha,hi,ho'
>>> ser.str.cat()
'hahiho'
- 손실값은 기본으로 무시한다.
- na_rep를 사용하면 원하는 값을 이어 붙일 수 있다.
>>> ser1 = pd.Series(['ha', np.nan, 'hi'])
>>> ser1.str.cat(sep=',')
'ha,hi'
>>> ser1.str.cat(sep=',', na_rep='*')
'ha,*,hi'
- cat() 메소드에 인수를 전달하면 호출하는 시리즈의 각 요소에 덧붙일 수 있다.
>>> ser.str.cat(['A', 'I', 'O'])
0 haA
1 hiI
2 hoO
dtype: object
- 손실값이 있고, na_rep 옵션을 명시하지 않으면 손실값이 결과에 표시된다.
- na_rep 옵션을 명시하면 입력한 부분을 덧붙인다.
>>> ser.str.cat(ser1)
0 haha
1 NaN
2 hohi
dtype: object
>>> ser.str.cat(ser1, na_rep='*')
0 haha
1 hi*
2 hohi
dtype: object
- 다음 예제에서 ser과 ser1을 판다스의 concat() 함수를 이용해 axis=1, 즉 열 기준으로 이어 붙이면 2차원 데이터프레임이 된다.
df = pd.concat([ser1, ser], axis=1)
df
>>> ser.str.cat(df, na_rep='*')
0 hahaha
1 hi*hi
2 hohiho
dtype: object
- 길이가 같지 않은 객체들을 이어 붙일 때 join 옵션을 사용할 수 있다.
>>> ser2 = pd.Series(['z', 'a', 'b', 'd'], index=[-1, 0, 1, 3])
>>> ser2
-1 z
0 a
1 b
3 d
dtype: object
>>> ser
0 ha
1 hi
2 ho
dtype: object
>>> ser.str.cat(ser2, join='left', na_rep='*')
0 haa
1 hib
2 ho*
dtype: object
>>> ser.str.cat(ser2, join='outer', na_rep='*')
-1 *z
0 haa
1 hib
2 ho*
3 *d
dtype: object
str로 인덱스 변경하기
- [ ] 표기법을 사용하면 인덱스 위치를 직접 선택할 수 있다.
>>> ser = pd.Series(['Suho', 'AB', np.nan, 'rabbit', 'C'])
>>> ser.str[0]
0 S
1 A
2 NaN
3 r
4 C
dtype: object
>>> ser.str[1]
0 u
1 B
2 NaN
3 a
4 NaN
dtype: object
일기 형식의 텍스트 데이터 가공
- 일과를 표현한 일기 형식의 텍스트를 예제와 같이 생성해 원하는 형태의 데이터로 가공해본다.
day_plan = ["1st_seq: getting up at 05:45am",
"2nd_seq: swimming from 06:00am to 07:00am",
"3rd_seq: My morning food is American style",
"4th_seq: Writing some proposal from 02:00pm to 06:00pm",
"5th_seq: Arriving at JongGak at 07:00pm",
"6th_seq: Fun with friends enjoying beer till 09:30pm",
"7th_seq: My house at 10:30pm and sleeping by 12:00pm"]
df = pd.DataFrame(day_plan, columns=['schedule'])
df
- split() 메소드를 이용해 문자열을 리스트형으로 분할한다.
>>> df['schedule'].str.split()
0 [1st_seq:, getting, up, at, 05:45am]
1 [2nd_seq:, swimming, from, 06:00am, to, 07:00am]
2 [3rd_seq:, My, morning, food, is, American, st...
3 [4th_seq:, Writing, some, proposal, from, 02:0...
4 [5th_seq:, Arriving, at, JongGak, at, 07:00pm]
5 [6th_seq:, Fun, with, friends, enjoying, beer,...
6 [7th_seq:, My, house, at, 10:30pm, and, sleepi...
Name: schedule, dtype: object
- 데이터프레임에서 분할한 각 문자열의 수를 연산한다.
>>> df['schedule'].str.split().str.len()
0 5
1 6
2 7
3 8
4 6
5 8
6 9
Name: schedule, dtype: int64
- 문자열이 단어 my를 포함하는지 불리언으로 확인한다.
>>> df['schedule'].str.contains('My')
0 False
1 False
2 True
3 False
4 False
5 False
6 True
Name: schedule, dtype: bool
- 각 문자열에 숫자가 몇 개 있는지 연산한다.
>>> df['schedule'].str.count('\d')
0 5
1 9
2 1
3 9
4 5
5 5
6 9
Name: schedule, dtype: int64
- 문자열에서 모든 숫자만 구한다.
>>> df['schedule'].str.findall('\d')
0 [1, 0, 5, 4, 5]
1 [2, 0, 6, 0, 0, 0, 7, 0, 0]
2 [3]
3 [4, 0, 2, 0, 0, 0, 6, 0, 0]
4 [5, 0, 7, 0, 0]
5 [6, 0, 9, 3, 0]
6 [7, 1, 0, 3, 0, 1, 2, 0, 0]
Name: schedule, dtype: object
- 정규 표현식 패턴에 매칭하는 연산 결과를 구한다.
>>> df['schedule'].str.findall('(\d\d):(\d\d)')
0 [(05, 45)]
1 [(06, 00), (07, 00)]
2 []
3 [(02, 00), (06, 00)]
4 [(07, 00)]
5 [(09, 30)]
6 [(10, 30), (12, 00)]
Name: schedule, dtype: object
- 글자 _seq를 제거하는 패턴을 적용해 결과를 기수 형태로 나타낸다.
>>> df['schedule'].str.replace(r'(\w+_seq\b)', lambda x: x.groups()[0][0:3])
0 1st: getting up at 05:45am
1 2nd: swimming from 06:00am to 07:00am
2 3rd: My morning food is American style
3 4th: Writing some proposal from 02:00pm to 06:...
4 5th: Arriving at JongGak at 07:00pm
5 6th: Fun with friends enjoying beer till 09:30pm
6 7th: My house at 10:30pm and sleeping by 12:00pm
Name: schedule, dtype: object
- 시간을 추출하기 위해 정규 표현식 패턴을 매칭한다.
df['schedule'].str.extract('(\d\d):(\d\d)')
- 전체 시간을 나타내기 위해 정규 표현식 패턴을 매칭해 연산한다.
df['schedule'].str.extractall('((\d?\d):(\d\d) ?([ap]m))')
- 전체 시간을 표시하면서 열을 시간 단위별로 나타내기 위해 다음과 같이 패턴을 매칭한다.
dfx = df['schedule'].str.extractall('(?P<times>(?P<hr>\d\d):(?P<min>\d\d)?(?P<periods>[ap]m))')
dfx
- 멀티 인덱스 객체를 출력해 상태를 확인하고 행과 열의 이름을 변경한다.
>>> dfx.index
MultiIndex([(0, 0),
(1, 0),
(1, 1),
(3, 0),
(3, 1),
(4, 0),
(5, 0),
(6, 0),
(6, 1)],
names=[None, 'match'])
>>> dfx.index = pd.MultiIndex(levels=[ ['one', 'two', 'three', 'four', 'five', 'six'], ['1st', '2nd']],
codes=[[0, 1, 1, 2, 2, 3, 4, 5, 5], [0, 0, 1, 0, 1, 0, 0, 0, 1]],
names=['step', 'match'])
- 마지막으로 가공된 데이터프레임 객체 dfx를 확인한다.
dfx
728x90
그리드형(광고전용)
'In-depth Study > Pandas' 카테고리의 다른 글
[Pandas] 수학 계산 (0) | 2022.05.31 |
---|---|
[Pandas] 데이터의 그룹 연산 (0) | 2022.05.30 |
[Pandas] 데이터 타입과 입출력 (0) | 2022.05.27 |
[Pandas] 데이터 처리 (0) | 2022.05.27 |
[Pandas] 판다스의 주요 기능 (0) | 2022.05.25 |
[Pandas] 판다스 데이터 구조 (1) | 2022.05.24 |
[Pandas] 판다스(Pandas) 개요 (0) | 2022.05.24 |