Pandas (6) - 데이터 정리
깔끔한 데이터
열과 피벗
넓은 데이터를 정리
-
melt()
지정한 열의 데이터를 모두 행으로 정리 -
melt 메서드의 인자
id_vars : 위치를 그대로 유지할 열의 이름을 지정
value_vars : 행으로 위치를 변경할 열의 이름을 지정
var_name : value_vars로 위치를 변경한 열의 이름을 지정(나중에 변경가능)
value_name : var_name으로 위치를 변경한 열의 데이터를 지정한 열의 이름을 지정(나중에 변경가능)
import pandas as pd
df = pd.read_csv('data/pew.csv') # 종교별 소득범위 자료
df.head(3)
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 |
data = pd.melt(df, id_vars='religion') # ignore_index: idx번호 새로 생성, defautl(True)
data.head(3)
# var_name값이 variable자리로, value_name값이 value자리로
religion | variable | value | |
---|---|---|---|
0 | Agnostic | <$10k | 27 |
1 | Atheist | <$10k | 12 |
2 | Buddhist | <$10k | 27 |
# column명 변경
# 바꿀 column의 수가 적을 경우
data.columns = ['religion', 'income', 'count']
data.sample()
religion | income | count | |
---|---|---|---|
125 | Unaffiliated | $75-100k | 407 |
# 바꿀 column의 수가 많을 경우(바꿀 column만 지정)
data.rename(columns={"income":"소득영역", "count":"갯수"}) # columns={'원본':'변경', ...} # inplace=True로 원본 적용
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
df = pd.read_csv('data/billboard.csv')
df.head(3)
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
df.columns
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')
df_melt = pd.melt(df, id_vars=['year', 'artist', 'track', 'time', 'date.entered'],
var_name='week', value_name='rating')
df_melt.head(3)
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 |
print("원본 데이터 : {}, melt된 데이터 : {}".format(df.shape, df_melt.shape))
원본 데이터 : (317, 81), melt된 데이터 : (24092, 7)
열 이름 관리하기
- 하나의 열이 여러 의미를 가지고 있는 경우
df = pd.read_csv("data/country_timeseries.csv")
df.sample()
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 |
df_melt = pd.melt(df, id_vars=['Date', 'Day'])
df_melt.sample()
Date | Day | variable | value | |
---|---|---|---|---|
844 | 4/4/2014 | 13 | Cases_Spain | NaN |
- split 메서드로 열 이름 분리하기
# Series에서 str을 분리해 split
# df_melt['컬럼명'] : 정상적인 방법
# df_melt.컬럼명 : 공백이 있을 시 불가, 안되는 경우도 있다. 두개가 같지않음
df_melt['status'] = df_melt.variable.str.split('_').str.get(0)
# .str : str에 적용할 수 있는 메서드들을 사용할 수 있게 타입 변경
# .split('_') : '_'를 기준으로분리되어 리스트로 리턴
# .str.get(0) : 앞쪽 문자열만 추출
# .str.get(0) : 뒤쪽 문자열만 추출
df_melt['country'] = df_melt.variable.str.split('_').str.get(1)
df_melt.sample(3)
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
df = pd.read_csv('data/weather.csv')
df.head(1)
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
df.columns
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')
df_melt = pd.melt(df, id_vars=['id', 'year', 'month', 'element'],
var_name='day', value_name='temp')
df_melt.head(1)
id | year | month | element | day | temp | |
---|---|---|---|---|---|---|
0 | MX17004 | 2010 | 1 | tmax | d1 | NaN |
# pivot_table : 행과 열의 위치를 다시 바꿔 정리
df_pivot = df_melt.pivot_table(index=['id', 'year', 'month', 'day'],
columns='element', values='temp')
df_pivot.head(3)
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 |
df_pivot.index
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'])
# idx 초기화, idx는 value로 변환된다
df_pivot.reset_index(inplace=True)
df_pivot.head(3)
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 |
df_pivot.index
RangeIndex(start=0, stop=33, step=1)
df_pivot.values
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)
중복 데이터 처리하기
df = pd.read_csv('data/billboard.csv')
df.sample()
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
df.columns
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')
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개의 데이터가 중복저장 -> 테이블을 분리, 필요하면 연결해서 사용
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 |
# 데이터의 갯수 확인
print("원본: {}, melt : {}".format(df.shape, df_melt.shape))
원본: (317, 81), melt : (24092, 7)
# 중복데이터 확인
df_melt[df_melt.track == 'Loser']
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
df_songs = df_melt[['year', 'artist', 'track', 'time']]
df_songs.head(3)
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 |
df_songs.shape
(24092, 4)
# 중복데이터 삭제
df_songs.drop_duplicates(inplace=True)
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)
df_songs.shape
(317, 4)
# 일련번호 부여
df_songs['id'] = range(len(df_songs))
df_songs.head(3)
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 |
# df_songs의 일련번호을 원본 데이터와 연결
df_ratings = df_melt.merge(df_songs, on=['year', 'artist', 'track', 'time']) # id column이 추가됨
df_ratings.head(3)
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 |
# 필요없는 column삭제
df_ratings = df_ratings[['id', 'date.entered', 'week', 'rating']]
df_ratings.head(3)
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 |
df_songs.head(3)
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 개발자 양성 과정 - 심선조 강사님의 강의를 정리한 내용입니다.
댓글남기기