별의 공부 블로그 🧑🏻‍💻
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()lsuffixrsuffix 인수를 적용하면 왼쪽 열 이름 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() 메소드에 indexcolumns 인수를 전달하면 재형성된 데이터프레임 객체를 결과로 반환한다.
  • 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() 함수를 사용하면 교차표를 생성하고 데이터프레임을 반환한다.
  • indexcolumns에는 유사 배열, 시리즈 또는 배열의 리스트나 시리즈의 리스트를 입력한다.
  • values 옵션에는 유사 배열을 전달한다.
  • values 옵션 배열과 aggfunc를 전달하지 않으면 기본으로 테이블의 도수를 계산한다.

 

  • 다음 예제에서는 그룹별 데이터의 도수를 나타내는 교차표를 생성한다.
    • 행 라벨과 열 라벨을 표시하려면 rownamescolnames 인수를 사용한다.
    • 인수를 명시하지 않으면 행과 열에 자동으로 라벨을 부여한다.
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 를 인수로 취한 NaN0.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_namevar_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 모듈을 임포트해 사용할 수 있다.
  • 간단히 말하자면, 정규 표현식은 찾기 패턴을 정의하는 문자들의 시퀀스이며, 패턴은 문자열을 찾는 알고리즘이다.
    • 명시하는 찾기 패턴이 문자열에 포함되는지 확인한다.
  • 정규 표현식은 코딩 스크립트를 줄여 경제적이고 간략하게 만들기 위해 사용한다.

 

정규 표현식 구문

 

 

텍스트 데이터 가공하기

  • 텍스트 데이터는 해석이 까다롭고 복잡하여 많은 문자와 기호 등을 포함하고 있어 원하는 형태로 가공하기 어렵다.
  • pandas.Seriespandas.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
⚠️AdBlock이 감지되었습니다. 원할한 페이지 표시를 위해 AdBlock을 꺼주세요.⚠️
starrykss
starrykss
별의 공부 블로그 🧑🏻‍💻


📖 Contents 📖