4일차
05 깔끔한 데이터 만들기¶
05-1 깔끔한 데이터란?¶
깔끔한 데이터는 R 커뮤니티의 해들리 위컴이 한 논문에서 처음 소개한 개념으로, 데이터셋을 구조화하는 프레임워크입니다. 이 프레임워크를 사용하면 데이터셋을 분석하고 시각화하기 쉽습니다. 즉, 데이터를 정리하는 가장 이상적인 목표라고 볼 수 있습니다.
깔끔한 데이터란
- 행은 관측값을 나타내야 한다.
- 열은 변수를 나타내야 한다
- 관측 단위별로 데이터 표를 구성해야 한다
R을 활용한 데이터 과학에서 표에 초점을 주고 깔끔한 데이터를 재정의
- 변수는 열로 나타내야 한다
- 관측값은 행으로 나타내야 한다
- 값은 셀로 나타내야 한다.
05-2 열 이름이 값일 때¶
하나의 열만 남기기¶
퓨 리서치 센터에서 공개한 미국의 소득과 종교 데이터셋을 사용하면 변수가 아닌 값을 포함한 열 이름을 처리하는 방법을 살펴봅니다. 이 데이터에는 결측값(NaN)이 있습니다. CSV 에서는 빈칸으로 나타냅니다.
In [1]:
import pandas as pd
pew = pd.read_csv('../data/pew.csv')
In [2]:
pew.iloc[:, 0:5]
Out[2]:
religion | <$10k | $10-20k | $20-30k | $30-40k | |
---|---|---|---|---|---|
0 | Agnostic | 27 | 34 | 60 | 81 |
1 | Atheist | 12 | 27 | 37 | 52 |
2 | Buddhist | 27 | 21 | 30 | 34 |
3 | Catholic | 418 | 617 | 732 | 670 |
4 | Don’t know/refused | 15 | 14 | 15 | 11 |
5 | Evangelical Prot | 575 | 869 | 1064 | 982 |
6 | Hindu | 1 | 9 | 7 | 9 |
7 | Historically Black Prot | 228 | 244 | 236 | 238 |
8 | Jehovah's Witness | 20 | 27 | 24 | 24 |
9 | Jewish | 19 | 19 | 25 | 25 |
10 | Mainline Prot | 289 | 495 | 619 | 655 |
11 | Mormon | 29 | 40 | 48 | 51 |
12 | Muslim | 6 | 7 | 9 | 10 |
13 | Orthodox | 13 | 17 | 23 | 32 |
14 | Other Christian | 9 | 7 | 11 | 13 |
15 | Other Faiths | 20 | 33 | 40 | 46 |
16 | Other World Religions | 5 | 2 | 3 | 4 |
17 | Unaffiliated | 217 | 299 | 374 | 365 |
In [3]:
pew_long = pew.melt(id_vars='religion')
pew_long
Out[3]:
religion | variable | value | |
---|---|---|---|
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 [4]:
pew_long = pew.melt(id_vars='religion', var_name="income", value_name="count")
pew_long
Out[4]:
religion | income | count | |
---|---|---|---|
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 [5]:
bilboard = pd.read_csv('../data/billboard.csv')
bilboard.iloc[0:5, 0:16]
Out[5]:
year | artist | track | time | date.entered | wk1 | wk2 | wk3 | wk4 | wk5 | wk6 | wk7 | wk8 | wk9 | wk10 | wk11 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2000 | 2 Pac | Baby Don't Cry (Keep... | 4:22 | 2000-02-26 | 87 | 82.0 | 72.0 | 77.0 | 87.0 | 94.0 | 99.0 | 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 |
2 | 2000 | 3 Doors Down | Kryptonite | 3:53 | 2000-04-08 | 81 | 70.0 | 68.0 | 67.0 | 66.0 | 57.0 | 54.0 | 53.0 | 51.0 | 51.0 | 51.0 |
3 | 2000 | 3 Doors Down | Loser | 4:24 | 2000-10-21 | 76 | 76.0 | 72.0 | 69.0 | 67.0 | 65.0 | 55.0 | 59.0 | 62.0 | 61.0 | 61.0 |
4 | 2000 | 504 Boyz | Wobble Wobble | 3:35 | 2000-04-15 | 57 | 34.0 | 25.0 | 17.0 | 17.0 | 31.0 | 36.0 | 49.0 | 53.0 | 57.0 | 64.0 |
In [6]:
billboard_long = bilboard.melt(
id_vars=["year", "artist", "track", "time", "date.entered"],
var_name="week",
value_name="rating"
)
billboard_long
Out[6]:
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 |
3 | 2000 | 3 Doors Down | Loser | 4:24 | 2000-10-21 | wk1 | 76.0 |
4 | 2000 | 504 Boyz | Wobble Wobble | 3:35 | 2000-04-15 | wk1 | 57.0 |
... | ... | ... | ... | ... | ... | ... | ... |
24087 | 2000 | Yankee Grey | Another Nine Minutes | 3:10 | 2000-04-29 | wk76 | NaN |
24088 | 2000 | Yearwood, Trisha | Real Live Woman | 3:55 | 2000-04-01 | wk76 | NaN |
24089 | 2000 | Ying Yang Twins | Whistle While You Tw... | 4:19 | 2000-03-18 | wk76 | NaN |
24090 | 2000 | Zombie Nation | Kernkraft 400 | 3:30 | 2000-09-02 | wk76 | NaN |
24091 | 2000 | matchbox twenty | Bent | 4:12 | 2000-04-29 | wk76 | NaN |
24092 rows × 7 columns
In [7]:
# 05-3 열 이름에 변수가 여러개 일 떄
ebola = pd.read_csv('../data/country_timeseries.csv')
ebola.columns
Out[7]:
Index(['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'], dtype='object')
In [8]:
ebola.iloc[:5, [0, 1, 2, 10]]
Out[8]:
Date | Day | Cases_Guinea | Deaths_Guinea | |
---|---|---|---|---|
0 | 1/5/2015 | 289 | 2776.0 | 1786.0 |
1 | 1/4/2015 | 288 | 2775.0 | 1781.0 |
2 | 1/3/2015 | 287 | 2769.0 | 1767.0 |
3 | 1/2/2015 | 286 | NaN | NaN |
4 | 12/31/2014 | 284 | 2730.0 | 1739.0 |
In [9]:
ebola_long = ebola.melt(id_vars=['Date', 'Day'])
ebola_long
Out[9]:
Date | Day | variable | value | |
---|---|---|---|---|
0 | 1/5/2015 | 289 | Cases_Guinea | 2776.0 |
1 | 1/4/2015 | 288 | Cases_Guinea | 2775.0 |
2 | 1/3/2015 | 287 | Cases_Guinea | 2769.0 |
3 | 1/2/2015 | 286 | Cases_Guinea | NaN |
4 | 12/31/2014 | 284 | Cases_Guinea | 2730.0 |
... | ... | ... | ... | ... |
1947 | 3/27/2014 | 5 | Deaths_Mali | NaN |
1948 | 3/26/2014 | 4 | Deaths_Mali | NaN |
1949 | 3/25/2014 | 3 | Deaths_Mali | NaN |
1950 | 3/24/2014 | 2 | Deaths_Mali | NaN |
1951 | 3/22/2014 | 0 | Deaths_Mali | NaN |
1952 rows × 4 columns
In [13]:
ebola_long[['variable']].groupby('variable').all()
Out[13]:
variable |
---|
Cases_Guinea |
Cases_Liberia |
Cases_Mali |
Cases_Nigeria |
Cases_Senegal |
Cases_SierraLeone |
Cases_Spain |
Cases_UnitedStates |
Deaths_Guinea |
Deaths_Liberia |
Deaths_Mali |
Deaths_Nigeria |
Deaths_Senegal |
Deaths_SierraLeone |
Deaths_Spain |
Deaths_UnitedStates |
In [18]:
variable_split = ebola_long.variable.str.split('_')
variable_split[:5]
Out[18]:
0 [Cases, Guinea] 1 [Cases, Guinea] 2 [Cases, Guinea] 3 [Cases, Guinea] 4 [Cases, Guinea] Name: variable, dtype: object
In [19]:
status_values = variable_split.str.get(0)
country_values = variable_split.str.get(1)
status_values
Out[19]:
0 Cases 1 Cases 2 Cases 3 Cases 4 Cases ... 1947 Deaths 1948 Deaths 1949 Deaths 1950 Deaths 1951 Deaths Name: variable, Length: 1952, dtype: object
In [20]:
ebola_long['status'] = status_values
ebola_long['country'] = country_values
ebola_long
Out[20]:
Date | Day | variable | value | status | country | |
---|---|---|---|---|---|---|
0 | 1/5/2015 | 289 | Cases_Guinea | 2776.0 | Cases | Guinea |
1 | 1/4/2015 | 288 | Cases_Guinea | 2775.0 | Cases | Guinea |
2 | 1/3/2015 | 287 | Cases_Guinea | 2769.0 | Cases | Guinea |
3 | 1/2/2015 | 286 | Cases_Guinea | NaN | Cases | Guinea |
4 | 12/31/2014 | 284 | Cases_Guinea | 2730.0 | Cases | Guinea |
... | ... | ... | ... | ... | ... | ... |
1947 | 3/27/2014 | 5 | Deaths_Mali | NaN | Deaths | Mali |
1948 | 3/26/2014 | 4 | Deaths_Mali | NaN | Deaths | Mali |
1949 | 3/25/2014 | 3 | Deaths_Mali | NaN | Deaths | Mali |
1950 | 3/24/2014 | 2 | Deaths_Mali | NaN | Deaths | Mali |
1951 | 3/22/2014 | 0 | Deaths_Mali | NaN | Deaths | Mali |
1952 rows × 6 columns
In [21]:
ebola_long = ebola.melt(id_vars=['Date', 'Day'])
variable_split = ebola_long.variable.str.split('_', expand=True) # 분할과 동시에 데이터 프레임을 만듭니다.
variable_split
Out[21]:
0 | 1 | |
---|---|---|
0 | Cases | Guinea |
1 | Cases | Guinea |
2 | Cases | Guinea |
3 | Cases | Guinea |
4 | Cases | Guinea |
... | ... | ... |
1947 | Deaths | Mali |
1948 | Deaths | Mali |
1949 | Deaths | Mali |
1950 | Deaths | Mali |
1951 | Deaths | Mali |
1952 rows × 2 columns
In [22]:
ebola_long[['status', 'country']] = variable_split
ebola_long
Out[22]:
Date | Day | variable | value | status | country | |
---|---|---|---|---|---|---|
0 | 1/5/2015 | 289 | Cases_Guinea | 2776.0 | Cases | Guinea |
1 | 1/4/2015 | 288 | Cases_Guinea | 2775.0 | Cases | Guinea |
2 | 1/3/2015 | 287 | Cases_Guinea | 2769.0 | Cases | Guinea |
3 | 1/2/2015 | 286 | Cases_Guinea | NaN | Cases | Guinea |
4 | 12/31/2014 | 284 | Cases_Guinea | 2730.0 | Cases | Guinea |
... | ... | ... | ... | ... | ... | ... |
1947 | 3/27/2014 | 5 | Deaths_Mali | NaN | Deaths | Mali |
1948 | 3/26/2014 | 4 | Deaths_Mali | NaN | Deaths | Mali |
1949 | 3/25/2014 | 3 | Deaths_Mali | NaN | Deaths | Mali |
1950 | 3/24/2014 | 2 | Deaths_Mali | NaN | Deaths | Mali |
1951 | 3/22/2014 | 0 | Deaths_Mali | NaN | Deaths | Mali |
1952 rows × 6 columns
In [24]:
weather = pd.read_csv('../data/weather.csv')
weather.iloc[:5, :11]
Out[24]:
id | year | month | element | d1 | d2 | d3 | d4 | d5 | d6 | d7 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | MX17004 | 2010 | 1 | tmax | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | MX17004 | 2010 | 1 | tmin | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | MX17004 | 2010 | 2 | tmax | NaN | 27.3 | 24.1 | NaN | NaN | NaN | NaN |
3 | MX17004 | 2010 | 2 | tmin | NaN | 14.4 | 14.4 | NaN | NaN | NaN | NaN |
4 | MX17004 | 2010 | 3 | tmax | NaN | NaN | NaN | NaN | 32.1 | NaN | NaN |
In [25]:
weather_melt = weather.melt(
id_vars=["id", "year", "month", "element"],
var_name="day",
value_name="temp"
)
weather_melt
Out[25]:
id | year | month | element | day | temp | |
---|---|---|---|---|---|---|
0 | MX17004 | 2010 | 1 | tmax | d1 | NaN |
1 | MX17004 | 2010 | 1 | tmin | d1 | NaN |
2 | MX17004 | 2010 | 2 | tmax | d1 | NaN |
3 | MX17004 | 2010 | 2 | tmin | d1 | NaN |
4 | MX17004 | 2010 | 3 | tmax | d1 | NaN |
... | ... | ... | ... | ... | ... | ... |
677 | MX17004 | 2010 | 10 | tmin | d31 | NaN |
678 | MX17004 | 2010 | 11 | tmax | d31 | NaN |
679 | MX17004 | 2010 | 11 | tmin | d31 | NaN |
680 | MX17004 | 2010 | 12 | tmax | d31 | NaN |
681 | MX17004 | 2010 | 12 | tmin | d31 | NaN |
682 rows × 6 columns
In [26]:
weather_tidy = weather_melt.pivot_table(
index=['id', 'year', 'month', 'day'],
columns='element',
values='temp'
)
weather_tidy
Out[26]:
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 | |||
d23 | 29.9 | 10.7 | |||
d3 | 24.1 | 14.4 | |||
3 | d10 | 34.5 | 16.8 | ||
d16 | 31.1 | 17.6 | |||
d5 | 32.1 | 14.2 | |||
4 | d27 | 36.3 | 16.7 | ||
5 | d27 | 33.2 | 18.2 | ||
6 | d17 | 28.0 | 17.5 | ||
d29 | 30.1 | 18.0 | |||
7 | d3 | 28.6 | 17.5 | ||
d14 | 29.9 | 16.5 | |||
8 | d23 | 26.4 | 15.0 | ||
d5 | 29.6 | 15.8 | |||
d29 | 28.0 | 15.3 | |||
d13 | 29.8 | 16.5 | |||
d25 | 29.7 | 15.6 | |||
d31 | 25.4 | 15.4 | |||
d8 | 29.0 | 17.3 | |||
10 | d5 | 27.0 | 14.0 | ||
d14 | 29.5 | 13.0 | |||
d15 | 28.7 | 10.5 | |||
d28 | 31.2 | 15.0 | |||
d7 | 28.1 | 12.9 | |||
11 | d2 | 31.3 | 16.3 | ||
d5 | 26.3 | 7.9 | |||
d27 | 27.7 | 14.2 | |||
d26 | 28.1 | 12.1 | |||
d4 | 27.2 | 12.0 | |||
12 | d1 | 29.9 | 13.8 | ||
d6 | 27.8 | 10.5 |
In [27]:
weather_tidy_flat = weather_tidy.reset_index()
weather_tidy_flat
Out[27]:
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 |
3 | MX17004 | 2010 | 2 | d23 | 29.9 | 10.7 |
4 | MX17004 | 2010 | 2 | d3 | 24.1 | 14.4 |
5 | MX17004 | 2010 | 3 | d10 | 34.5 | 16.8 |
6 | MX17004 | 2010 | 3 | d16 | 31.1 | 17.6 |
7 | MX17004 | 2010 | 3 | d5 | 32.1 | 14.2 |
8 | MX17004 | 2010 | 4 | d27 | 36.3 | 16.7 |
9 | MX17004 | 2010 | 5 | d27 | 33.2 | 18.2 |
10 | MX17004 | 2010 | 6 | d17 | 28.0 | 17.5 |
11 | MX17004 | 2010 | 6 | d29 | 30.1 | 18.0 |
12 | MX17004 | 2010 | 7 | d3 | 28.6 | 17.5 |
13 | MX17004 | 2010 | 7 | d14 | 29.9 | 16.5 |
14 | MX17004 | 2010 | 8 | d23 | 26.4 | 15.0 |
15 | MX17004 | 2010 | 8 | d5 | 29.6 | 15.8 |
16 | MX17004 | 2010 | 8 | d29 | 28.0 | 15.3 |
17 | MX17004 | 2010 | 8 | d13 | 29.8 | 16.5 |
18 | MX17004 | 2010 | 8 | d25 | 29.7 | 15.6 |
19 | MX17004 | 2010 | 8 | d31 | 25.4 | 15.4 |
20 | MX17004 | 2010 | 8 | d8 | 29.0 | 17.3 |
21 | MX17004 | 2010 | 10 | d5 | 27.0 | 14.0 |
22 | MX17004 | 2010 | 10 | d14 | 29.5 | 13.0 |
23 | MX17004 | 2010 | 10 | d15 | 28.7 | 10.5 |
24 | MX17004 | 2010 | 10 | d28 | 31.2 | 15.0 |
25 | MX17004 | 2010 | 10 | d7 | 28.1 | 12.9 |
26 | MX17004 | 2010 | 11 | d2 | 31.3 | 16.3 |
27 | MX17004 | 2010 | 11 | d5 | 26.3 | 7.9 |
28 | MX17004 | 2010 | 11 | d27 | 27.7 | 14.2 |
29 | MX17004 | 2010 | 11 | d26 | 28.1 | 12.1 |
30 | MX17004 | 2010 | 11 | d4 | 27.2 | 12.0 |
31 | MX17004 | 2010 | 12 | d1 | 29.9 | 13.8 |
32 | MX17004 | 2010 | 12 | d6 | 27.8 | 10.5 |
In [28]:
weather_tidy = (
weather_melt
.pivot_table(
index=['id', 'year', 'month', 'day'],
columns='element',
values='temp'
)
.reset_index()
)
weather_tidy
Out[28]:
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 |
3 | MX17004 | 2010 | 2 | d23 | 29.9 | 10.7 |
4 | MX17004 | 2010 | 2 | d3 | 24.1 | 14.4 |
5 | MX17004 | 2010 | 3 | d10 | 34.5 | 16.8 |
6 | MX17004 | 2010 | 3 | d16 | 31.1 | 17.6 |
7 | MX17004 | 2010 | 3 | d5 | 32.1 | 14.2 |
8 | MX17004 | 2010 | 4 | d27 | 36.3 | 16.7 |
9 | MX17004 | 2010 | 5 | d27 | 33.2 | 18.2 |
10 | MX17004 | 2010 | 6 | d17 | 28.0 | 17.5 |
11 | MX17004 | 2010 | 6 | d29 | 30.1 | 18.0 |
12 | MX17004 | 2010 | 7 | d3 | 28.6 | 17.5 |
13 | MX17004 | 2010 | 7 | d14 | 29.9 | 16.5 |
14 | MX17004 | 2010 | 8 | d23 | 26.4 | 15.0 |
15 | MX17004 | 2010 | 8 | d5 | 29.6 | 15.8 |
16 | MX17004 | 2010 | 8 | d29 | 28.0 | 15.3 |
17 | MX17004 | 2010 | 8 | d13 | 29.8 | 16.5 |
18 | MX17004 | 2010 | 8 | d25 | 29.7 | 15.6 |
19 | MX17004 | 2010 | 8 | d31 | 25.4 | 15.4 |
20 | MX17004 | 2010 | 8 | d8 | 29.0 | 17.3 |
21 | MX17004 | 2010 | 10 | d5 | 27.0 | 14.0 |
22 | MX17004 | 2010 | 10 | d14 | 29.5 | 13.0 |
23 | MX17004 | 2010 | 10 | d15 | 28.7 | 10.5 |
24 | MX17004 | 2010 | 10 | d28 | 31.2 | 15.0 |
25 | MX17004 | 2010 | 10 | d7 | 28.1 | 12.9 |
26 | MX17004 | 2010 | 11 | d2 | 31.3 | 16.3 |
27 | MX17004 | 2010 | 11 | d5 | 26.3 | 7.9 |
28 | MX17004 | 2010 | 11 | d27 | 27.7 | 14.2 |
29 | MX17004 | 2010 | 11 | d26 | 28.1 | 12.1 |
30 | MX17004 | 2010 | 11 | d4 | 27.2 | 12.0 |
31 | MX17004 | 2010 | 12 | d1 | 29.9 | 13.8 |
32 | MX17004 | 2010 | 12 | d6 | 27.8 | 10.5 |
'도서 > 프로그래밍' 카테고리의 다른 글
[06] Do it! 데이터 분석을 위한 판다스 입문 (0) | 2024.01.07 |
---|---|
[05] Do it! 데이터 분석을 위한 판다스 입문 (0) | 2024.01.06 |
[03] Do it! 데이터 분석을 위한 판다스 입문 (0) | 2024.01.04 |
[02] Do it! 데이터 분석을 위한 판다스 입문 (0) | 2024.01.03 |
[01] Do it! 데이터 분석을 위한 판다스 입문 (2) | 2024.01.02 |