7 분 소요


깔끔한 데이터

열과 피벗

넓은 데이터를 정리

  • melt()
    지정한 열의 데이터를 모두 행으로 정리

  • melt 메서드의 인자
    id_vars : 위치를 그대로 유지할 열의 이름을 지정
    value_vars : 행으로 위치를 변경할 열의 이름을 지정
    var_name : value_vars로 위치를 변경한 열의 이름을 지정(나중에 변경가능)
    value_name : var_name으로 위치를 변경한 열의 데이터를 지정한 열의 이름을 지정(나중에 변경가능)

In [1]:
import pandas as pd
In [2]:
df = pd.read_csv('data/pew.csv') # 종교별 소득범위 자료
df.head(3)
Out [2]:
religion <$10k $10-20k $20-30k $30-40k $40-50k $50-75k $75-100k $100-150k >150k Don't know/refused
0 Agnostic 27 34 60 81 76 137 122 109 84 96
1 Atheist 12 27 37 52 35 70 73 59 74 76
2 Buddhist 27 21 30 34 33 58 62 39 53 54
In [3]:
data = pd.melt(df, id_vars='religion') # ignore_index: idx번호 새로 생성, defautl(True)
data.head(3)
# var_name값이 variable자리로, value_name값이 value자리로
Out [3]:
religion variable value
0 Agnostic <$10k 27
1 Atheist <$10k 12
2 Buddhist <$10k 27
In [4]:
# column명 변경
# 바꿀 column의 수가 적을 경우
data.columns = ['religion', 'income', 'count']
data.sample()
Out [4]:
religion income count
125 Unaffiliated $75-100k 407
In [5]:
# 바꿀 column의 수가 많을 경우(바꿀 column만 지정)
data.rename(columns={"income":"소득영역", "count":"갯수"}) # columns={'원본':'변경', ...} # inplace=True로 원본 적용
Out [5]:
religion 소득영역 갯수
0 Agnostic <$10k 27
1 Atheist <$10k 12
2 Buddhist <$10k 27
3 Catholic <$10k 418
4 Don’t know/refused <$10k 15
... ... ... ...
175 Orthodox Don't know/refused 73
176 Other Christian Don't know/refused 18
177 Other Faiths Don't know/refused 71
178 Other World Religions Don't know/refused 8
179 Unaffiliated Don't know/refused 597

180 rows × 3 columns

In [6]:
df = pd.read_csv('data/billboard.csv')
df.head(3)
Out [6]:
year artist track time date.entered wk1 wk2 wk3 wk4 wk5 ... wk67 wk68 wk69 wk70 wk71 wk72 wk73 wk74 wk75 wk76
0 2000 2 Pac Baby Don't Cry (Keep... 4:22 2000-02-26 87 82.0 72.0 77.0 87.0 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2000 2Ge+her The Hardest Part Of ... 3:15 2000-09-02 91 87.0 92.0 NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 2000 3 Doors Down Kryptonite 3:53 2000-04-08 81 70.0 68.0 67.0 66.0 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

3 rows × 81 columns

In [7]:
df.columns
Out [7]:
Index(['year', 'artist', 'track', 'time', 'date.entered', 'wk1', 'wk2', 'wk3',
       'wk4', 'wk5', 'wk6', 'wk7', 'wk8', 'wk9', 'wk10', 'wk11', 'wk12',
       'wk13', 'wk14', 'wk15', 'wk16', 'wk17', 'wk18', 'wk19', 'wk20', 'wk21',
       'wk22', 'wk23', 'wk24', 'wk25', 'wk26', 'wk27', 'wk28', 'wk29', 'wk30',
       'wk31', 'wk32', 'wk33', 'wk34', 'wk35', 'wk36', 'wk37', 'wk38', 'wk39',
       'wk40', 'wk41', 'wk42', 'wk43', 'wk44', 'wk45', 'wk46', 'wk47', 'wk48',
       'wk49', 'wk50', 'wk51', 'wk52', 'wk53', 'wk54', 'wk55', 'wk56', 'wk57',
       'wk58', 'wk59', 'wk60', 'wk61', 'wk62', 'wk63', 'wk64', 'wk65', 'wk66',
       'wk67', 'wk68', 'wk69', 'wk70', 'wk71', 'wk72', 'wk73', 'wk74', 'wk75',
       'wk76'],
      dtype='object')
In [8]:
df_melt = pd.melt(df, id_vars=['year', 'artist', 'track', 'time', 'date.entered'],
        var_name='week', value_name='rating')
df_melt.head(3)
Out [8]:
year artist track time date.entered week rating
0 2000 2 Pac Baby Don't Cry (Keep... 4:22 2000-02-26 wk1 87.0
1 2000 2Ge+her The Hardest Part Of ... 3:15 2000-09-02 wk1 91.0
2 2000 3 Doors Down Kryptonite 3:53 2000-04-08 wk1 81.0
In [9]:
print("원본 데이터 : {}, melt된 데이터 : {}".format(df.shape, df_melt.shape))
Out [9]:
원본 데이터 : (317, 81), melt된 데이터 : (24092, 7)

열 이름 관리하기

  • 하나의 열이 여러 의미를 가지고 있는 경우
In [10]:
df = pd.read_csv("data/country_timeseries.csv")
df.sample()
Out [10]:
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone Cases_Nigeria Cases_Senegal Cases_UnitedStates Cases_Spain Cases_Mali Deaths_Guinea Deaths_Liberia Deaths_SierraLeone Deaths_Nigeria Deaths_Senegal Deaths_UnitedStates Deaths_Spain Deaths_Mali
34 10/21/2014 213 1553.0 NaN NaN NaN NaN NaN NaN NaN 926.0 NaN NaN NaN NaN NaN NaN NaN
In [11]:
df_melt = pd.melt(df, id_vars=['Date', 'Day'])
df_melt.sample()
Out [11]:
Date Day variable value
844 4/4/2014 13 Cases_Spain NaN
  • split 메서드로 열 이름 분리하기
In [12]:
# Series에서 str을 분리해 split
# df_melt['컬럼명'] : 정상적인 방법
# df_melt.컬럼명 : 공백이 있을 시 불가, 안되는 경우도 있다. 두개가 같지않음
df_melt['status'] = df_melt.variable.str.split('_').str.get(0)
# .str : str에 적용할 수 있는 메서드들을 사용할 수 있게 타입 변경
# .split('_') : '_'를 기준으로분리되어 리스트로 리턴
# .str.get(0) : 앞쪽 문자열만 추출
In [13]:
# .str.get(0) : 뒤쪽 문자열만 추출
df_melt['country'] = df_melt.variable.str.split('_').str.get(1)
In [14]:
df_melt.sample(3)
Out [14]:
Date Day variable value status country
1850 11/16/2014 239 Deaths_Mali 5.0 Deaths Mali
914 8/20/2014 151 Cases_Mali NaN Cases Mali
161 10/12/2014 204 Cases_Liberia NaN Cases Liberia

여러 열을 하나로 정리하기

  • 기상데이터 여러열을 하나로 정리 - melt, pivot_talbe
In [15]:
df = pd.read_csv('data/weather.csv')
df.head(1)
Out [15]:
id year month element d1 d2 d3 d4 d5 d6 ... d22 d23 d24 d25 d26 d27 d28 d29 d30 d31
0 MX17004 2010 1 tmax NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN 27.8 NaN

1 rows × 35 columns

In [16]:
df.columns
Out [16]:
Index(['id', 'year', 'month', 'element', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6',
       'd7', 'd8', 'd9', 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16',
       'd17', 'd18', 'd19', 'd20', 'd21', 'd22', 'd23', 'd24', 'd25', 'd26',
       'd27', 'd28', 'd29', 'd30', 'd31'],
      dtype='object')
In [17]:
df_melt = pd.melt(df, id_vars=['id', 'year', 'month', 'element'],
                  var_name='day', value_name='temp')
df_melt.head(1)
Out [17]:
id year month element day temp
0 MX17004 2010 1 tmax d1 NaN
In [18]:
# pivot_table : 행과 열의 위치를 다시 바꿔 정리
df_pivot = df_melt.pivot_table(index=['id', 'year', 'month', 'day'],
                               columns='element', values='temp')
df_pivot.head(3)
Out [18]:
element tmax tmin
id year month day
MX17004 2010 1 d30 27.8 14.5
2 d11 29.7 13.4
d2 27.3 14.4
In [19]:
df_pivot.index
Out [19]:
MultiIndex([('MX17004', 2010,  1, 'd30'),
            ('MX17004', 2010,  2, 'd11'),
            ('MX17004', 2010,  2,  'd2'),
            ('MX17004', 2010,  2, 'd23'),
            ('MX17004', 2010,  2,  'd3'),
            ('MX17004', 2010,  3, 'd10'),
            ('MX17004', 2010,  3, 'd16'),
            ('MX17004', 2010,  3,  'd5'),
            ('MX17004', 2010,  4, 'd27'),
            ('MX17004', 2010,  5, 'd27'),
            ('MX17004', 2010,  6, 'd17'),
            ('MX17004', 2010,  6, 'd29'),
            ('MX17004', 2010,  7,  'd3'),
            ('MX17004', 2010,  7, 'd14'),
            ('MX17004', 2010,  8, 'd23'),
            ('MX17004', 2010,  8,  'd5'),
            ('MX17004', 2010,  8, 'd29'),
            ('MX17004', 2010,  8, 'd13'),
            ('MX17004', 2010,  8, 'd25'),
            ('MX17004', 2010,  8, 'd31'),
            ('MX17004', 2010,  8,  'd8'),
            ('MX17004', 2010, 10,  'd5'),
            ('MX17004', 2010, 10, 'd14'),
            ('MX17004', 2010, 10, 'd15'),
            ('MX17004', 2010, 10, 'd28'),
            ('MX17004', 2010, 10,  'd7'),
            ('MX17004', 2010, 11,  'd2'),
            ('MX17004', 2010, 11,  'd5'),
            ('MX17004', 2010, 11, 'd27'),
            ('MX17004', 2010, 11, 'd26'),
            ('MX17004', 2010, 11,  'd4'),
            ('MX17004', 2010, 12,  'd1'),
            ('MX17004', 2010, 12,  'd6')],
           names=['id', 'year', 'month', 'day'])
In [20]:
# idx 초기화, idx는 value로 변환된다
df_pivot.reset_index(inplace=True)
df_pivot.head(3)
Out [20]:
element id year month day tmax tmin
0 MX17004 2010 1 d30 27.8 14.5
1 MX17004 2010 2 d11 29.7 13.4
2 MX17004 2010 2 d2 27.3 14.4
In [21]:
df_pivot.index
Out [21]:
RangeIndex(start=0, stop=33, step=1)
In [22]:
df_pivot.values
Out [22]:
array([['MX17004', 2010, 1, 'd30', 27.8, 14.5],
       ['MX17004', 2010, 2, 'd11', 29.7, 13.4],
       ['MX17004', 2010, 2, 'd2', 27.3, 14.4],
       ['MX17004', 2010, 2, 'd23', 29.9, 10.7],
       ['MX17004', 2010, 2, 'd3', 24.1, 14.4],
       ['MX17004', 2010, 3, 'd10', 34.5, 16.8],
       ['MX17004', 2010, 3, 'd16', 31.1, 17.6],
       ['MX17004', 2010, 3, 'd5', 32.1, 14.2],
       ['MX17004', 2010, 4, 'd27', 36.3, 16.7],
       ['MX17004', 2010, 5, 'd27', 33.2, 18.2],
       ['MX17004', 2010, 6, 'd17', 28.0, 17.5],
       ['MX17004', 2010, 6, 'd29', 30.1, 18.0],
       ['MX17004', 2010, 7, 'd3', 28.6, 17.5],
       ['MX17004', 2010, 7, 'd14', 29.9, 16.5],
       ['MX17004', 2010, 8, 'd23', 26.4, 15.0],
       ['MX17004', 2010, 8, 'd5', 29.6, 15.8],
       ['MX17004', 2010, 8, 'd29', 28.0, 15.3],
       ['MX17004', 2010, 8, 'd13', 29.8, 16.5],
       ['MX17004', 2010, 8, 'd25', 29.7, 15.6],
       ['MX17004', 2010, 8, 'd31', 25.4, 15.4],
       ['MX17004', 2010, 8, 'd8', 29.0, 17.3],
       ['MX17004', 2010, 10, 'd5', 27.0, 14.0],
       ['MX17004', 2010, 10, 'd14', 29.5, 13.0],
       ['MX17004', 2010, 10, 'd15', 28.7, 10.5],
       ['MX17004', 2010, 10, 'd28', 31.2, 15.0],
       ['MX17004', 2010, 10, 'd7', 28.1, 12.9],
       ['MX17004', 2010, 11, 'd2', 31.3, 16.3],
       ['MX17004', 2010, 11, 'd5', 26.3, 7.9],
       ['MX17004', 2010, 11, 'd27', 27.7, 14.2],
       ['MX17004', 2010, 11, 'd26', 28.1, 12.1],
       ['MX17004', 2010, 11, 'd4', 27.2, 12.0],
       ['MX17004', 2010, 12, 'd1', 29.9, 13.8],
       ['MX17004', 2010, 12, 'd6', 27.8, 10.5]], dtype=object)

중복 데이터 처리하기

In [23]:
df = pd.read_csv('data/billboard.csv')
df.sample()
Out [23]:
year artist track time date.entered wk1 wk2 wk3 wk4 wk5 ... wk67 wk68 wk69 wk70 wk71 wk72 wk73 wk74 wk75 wk76
175 2000 Lil' Zane Callin' Me 3:43 2000-07-29 83 89.0 57.0 40.0 34.0 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

1 rows × 81 columns

In [24]:
df.columns
Out [24]:
Index(['year', 'artist', 'track', 'time', 'date.entered', 'wk1', 'wk2', 'wk3',
       'wk4', 'wk5', 'wk6', 'wk7', 'wk8', 'wk9', 'wk10', 'wk11', 'wk12',
       'wk13', 'wk14', 'wk15', 'wk16', 'wk17', 'wk18', 'wk19', 'wk20', 'wk21',
       'wk22', 'wk23', 'wk24', 'wk25', 'wk26', 'wk27', 'wk28', 'wk29', 'wk30',
       'wk31', 'wk32', 'wk33', 'wk34', 'wk35', 'wk36', 'wk37', 'wk38', 'wk39',
       'wk40', 'wk41', 'wk42', 'wk43', 'wk44', 'wk45', 'wk46', 'wk47', 'wk48',
       'wk49', 'wk50', 'wk51', 'wk52', 'wk53', 'wk54', 'wk55', 'wk56', 'wk57',
       'wk58', 'wk59', 'wk60', 'wk61', 'wk62', 'wk63', 'wk64', 'wk65', 'wk66',
       'wk67', 'wk68', 'wk69', 'wk70', 'wk71', 'wk72', 'wk73', 'wk74', 'wk75',
       'wk76'],
      dtype='object')
In [25]:
df_melt = pd.melt(df, id_vars=['year', 'artist', 'track', 'time', 'date.entered'],
        var_name='week', value_name='rating')
df_melt.head(3)
# 같은 노래가 76주동안 나옴 -> 76개의 데이터가 중복저장 -> 테이블을 분리, 필요하면 연결해서 사용
Out [25]:
year artist track time date.entered week rating
0 2000 2 Pac Baby Don't Cry (Keep... 4:22 2000-02-26 wk1 87.0
1 2000 2Ge+her The Hardest Part Of ... 3:15 2000-09-02 wk1 91.0
2 2000 3 Doors Down Kryptonite 3:53 2000-04-08 wk1 81.0
In [26]:
# 데이터의 갯수 확인
print("원본: {}, melt : {}".format(df.shape, df_melt.shape))
Out [26]:
원본: (317, 81), melt : (24092, 7)

In [27]:
# 중복데이터 확인
df_melt[df_melt.track == 'Loser']
Out [27]:
year artist track time date.entered week rating
3 2000 3 Doors Down Loser 4:24 2000-10-21 wk1 76.0
320 2000 3 Doors Down Loser 4:24 2000-10-21 wk2 76.0
637 2000 3 Doors Down Loser 4:24 2000-10-21 wk3 72.0
954 2000 3 Doors Down Loser 4:24 2000-10-21 wk4 69.0
1271 2000 3 Doors Down Loser 4:24 2000-10-21 wk5 67.0
... ... ... ... ... ... ... ...
22510 2000 3 Doors Down Loser 4:24 2000-10-21 wk72 NaN
22827 2000 3 Doors Down Loser 4:24 2000-10-21 wk73 NaN
23144 2000 3 Doors Down Loser 4:24 2000-10-21 wk74 NaN
23461 2000 3 Doors Down Loser 4:24 2000-10-21 wk75 NaN
23778 2000 3 Doors Down Loser 4:24 2000-10-21 wk76 NaN

76 rows × 7 columns

In [28]:
df_songs = df_melt[['year', 'artist', 'track', 'time']]
df_songs.head(3)
Out [28]:
year artist track time
0 2000 2 Pac Baby Don't Cry (Keep... 4:22
1 2000 2Ge+her The Hardest Part Of ... 3:15
2 2000 3 Doors Down Kryptonite 3:53
In [29]:
df_songs.shape
Out [29]:
(24092, 4)
In [30]:
# 중복데이터 삭제
df_songs.drop_duplicates(inplace=True)
Out [30]:
C:\Users\user\AppData\Local\Temp\ipykernel_11812\1962997251.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_songs.drop_duplicates(inplace=True)

In [31]:
df_songs.shape
Out [31]:
(317, 4)
In [32]:
# 일련번호 부여
df_songs['id'] = range(len(df_songs))
df_songs.head(3)
Out [32]:
C:\Users\user\AppData\Local\Temp\ipykernel_11812\3294717313.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_songs['id'] = range(len(df_songs))

year artist track time id
0 2000 2 Pac Baby Don't Cry (Keep... 4:22 0
1 2000 2Ge+her The Hardest Part Of ... 3:15 1
2 2000 3 Doors Down Kryptonite 3:53 2
In [33]:
# df_songs의 일련번호을 원본 데이터와 연결
df_ratings = df_melt.merge(df_songs, on=['year', 'artist', 'track', 'time']) # id column이 추가됨
df_ratings.head(3)
Out [33]:
year artist track time date.entered week rating id
0 2000 2 Pac Baby Don't Cry (Keep... 4:22 2000-02-26 wk1 87.0 0
1 2000 2 Pac Baby Don't Cry (Keep... 4:22 2000-02-26 wk2 82.0 0
2 2000 2 Pac Baby Don't Cry (Keep... 4:22 2000-02-26 wk3 72.0 0
In [34]:
# 필요없는 column삭제
df_ratings = df_ratings[['id', 'date.entered', 'week', 'rating']]
df_ratings.head(3)
Out [34]:
id date.entered week rating
0 0 2000-02-26 wk1 87.0
1 0 2000-02-26 wk2 82.0
2 0 2000-02-26 wk3 72.0
In [35]:
df_songs.head(3)
Out [35]:
year artist track time id
0 2000 2 Pac Baby Don't Cry (Keep... 4:22 0
1 2000 2Ge+her The Hardest Part Of ... 3:15 1
2 2000 3 Doors Down Kryptonite 3:53 2

id로 연결되는 df_ratings와 df_songs 두 데이터로 나눠짐!

Reference

  • 이 포스트는 SeSAC 인공지능 자연어처리, 컴퓨터비전 기술을 활용한 응용 SW 개발자 양성 과정 - 심선조 강사님의 강의를 정리한 내용입니다.

댓글남기기