์ด๋ฒ ๊ฐ์๋ "Bad Data"๋ผ๋ ๋์ ์ฃผ์ ์์ต๋๋ค. ์ค๋ฌด ๋ฐ์ดํฐ๊ฐ ์ ์ง์ ๋ถํ์ง(์คํยท๊ฒฐ์ธกยท์ค๋ณตยท๋นํ์คยท์ด์์นโฆ), ๊ทธ๊ฑธ ์ด๋ป๊ฒ ์ฒญ์ํ๊ณ (cleaning) ์ฃผ๋ฌด๋ฅด๋์ง(wrangling), ์๋ฌ๋ฅผ ์ด๋ป๊ฒ ํ์งยท์๋ฐฉํ๋์ง๊น์ง ํ๊ณ , ๋ง์ง๋ง์ Tidy Data๋ก ๋ง๋ฌด๋ฆฌํ๋๋ผ๊ณ ์. ๊ทธ๋ฆฌ๊ณ ๊ฐ์์ ํจ๊ป ๋ฐ์ tidy-data-master ํด๋์๋ ์ด Tidy Data ๊ฐ๋
์ ์์ โ Hadley Wickham(ํด๋ค๋ฆฌ ์์ปด)์ ๋
ผ๋ฌธ๊ณผ ์ค์ ์ง์ ๋ถํ ๋ฐ์ดํฐ ํ์ผ๋ค(์ข
๊ตยท์๋ ์กฐ์ฌ, ๋น๋ณด๋ ์ฐจํธ, ๊ฒฐํต ํต๊ณ, ๊ธฐ์ ๊ด์ธก)์ด ๋ค์ด ์์์ต๋๋ค.
๊ฐ์ ์ ์ฒด๋ฅผ ๋ค ์ฎ๊ธฐ๋ฉด ๋ฌผํ์ง๋๊น, ์ด ๊ธ์ "์ง์ ๋ถํ ํ๋ฅผ ์ด๋ป๊ฒ ๋ถ์ ๊ฐ๋ฅํ ํ๋ก ๋ฐ๊พธ๋๊ฐ" ํ ๊ฐ๋์ ์ง์คํฉ๋๋ค. Tidy Data๊ฐ ๋ญ์ง ์ ์๋ถํฐ ์ก๊ณ , ์์ปด์ด ์ ๋ฆฌํ ์ง์ ๋ถํ ๋ฐ์ดํฐ 5๋ ์ ํ ์ค ์์ฃผ ๋ง๋๋ 3๊ฐ์ง๋ฅผ ์ค์ ๊ฐ์ ๋ฐ์ดํฐ์ pandas๋ก ์ง์ ์๋ด๊ฐ๋ฉฐ ๋ณผ๊ฒ์. ์ฐธ๊ณ ๋ก ํด๋์ README๋ R ์ฝ๋ ํ์ผ(tidy-data.R)์ ์ธ๊ธํ์ง๋ง ์ค์ ํ์ผ์ ๋น ์ ธ ์์ด์, ๋
ผ๋ฌธ์ R ์์ ๋ฅผ ์ ๊ฐ pandas๋ก ์ฌํํ์ต๋๋ค.
- ํฐ ๊ทธ๋ฆผ โ ์กฐ๊ฐ๋ค์ด ์ด๋ป๊ฒ ๋ง๋ฌผ๋ฆฌ๋
- Tidy Data๋ ๋ฌด์์ธ๊ฐ (3์์น)
- ์ง์ ๋ถํ ๋ฐ์ดํฐ 5๋ ์ ํ
- melt โ ์ด ์ด๋ฆ์ด ๊ฐ์ผ ๋ (์ข ๊ตยท์๋ ๋ฐ์ดํฐ)
- split โ ํ ์ด์ ๋ณ์๊ฐ ๋ ๊ฐ์ผ ๋ (๊ฒฐํต ๋ฐ์ดํฐ)
- pivot โ ๊ฐ์ด ํยท์ด ์์ชฝ์ ํฉ์ด์ก์ ๋ (๊ธฐ์ ๋ฐ์ดํฐ)
- ์นํธ์ํธ & ํต์ฌ 4๊ฐ์ง
1. ํฐ ๊ทธ๋ฆผ โ ์กฐ๊ฐ๋ค์ด ์ด๋ป๊ฒ ๋ง๋ฌผ๋ฆฌ๋
Tidy Data๋ ๊ฒฐ๊ตญ "ํ ํ๋์ ๋ฑ ์ธ ๊ฐ์ง ์ง๋ฌธ์ ๋์ง๊ณ , ์ด๊ธ๋๋ฉด ์ ํด์ง ๋๊ตฌ๋ก ๊ณ ์น๋ค"๋ ์ด์ผ๊ธฐ์
๋๋ค. ์ธ ๊ฐ์ง ์ง๋ฌธ์ด tidy์ 3์์น์ด๊ณ , ์ด๊ธ๋๋ ๋ฐฉ์์ด ์ง์ ๋ถํ ์ ํ์ด๋ฉฐ, ๊ณ ์น๋ ๋๊ตฌ๊ฐ meltยทsplitยทpivot์
๋๋ค. ์๋ ์ง๋๊ฐ ์ด ๊ธ์ ๋ผ๋์์. ์ดํ ๊ฐ ์น์
์ด ์ด ์นธ์ ํ๋์ฉ ์ฑ์๋๋ค.
2. Tidy Data๋ ๋ฌด์์ธ๊ฐ (3์์น)
๋จผ์ ์ฉ์ด๋ถํฐ ์ ํํ ์ก๊ณ ๊ฐ์๋ค. ๊ฐ์ ์ฌ๋ผ์ด๋์๋ ํ ์ค๋ก "Tidy Data = Narrow Data = long form"์ด๋ผ๊ณ ๋ง ์ ํ ์์ด์, ์ด๊ฒ ์ ํํ ๋ญ ๋ปํ๋์ง ์์ ์์ ํ์ธํ๋ ๊ฒ ์ค์ํฉ๋๋ค.
Tidy Data (์ ๋๋ ๋ฐ์ดํฐ): ๋ฐ์ดํฐ์ ์๋ฏธ๋ฅผ ๊ตฌ์กฐ์ ๊ท์น์ ์ผ๋ก ๋์์ํจ ํ โ ๋ณ์๋ ์ด, ๊ด์ธก์น๋ ํ์ ์ค๋๋ก ๋ง์ถ ํํ.
variable (๋ณ์): ๊ฐ์ ์์ฑ์ ์ฌ๋ฌ ๋์์ ๊ฑธ์ณ ์ฐ ๊ฐ๋ค์ ๋ฌถ์ (์: ํค, ์จ๋, ์๋๊ตฌ๊ฐ). observation (๊ด์ธก์น): ํ ๋์์ ๋ํด ์ฐ ๊ฐ๋ค์ ๋ฌถ์ (์: ์ฌ๋ ํ ๋ช
, ํ๋ฃจ, ๊ฒฝ๊ธฐ ํ๋).
๋น์ ํ์๋ฉด, ์์
์์ ์ฌ๋์ด ๋ณด๊ธฐ ์ข์ผ๋ผ๊ณ ๋ง๋ "์์ฝํ"๋ ์
ํ๋์ ์ ๋ณด๊ฐ ์์ถ๋ผ ์์ด ๋์ผ๋ก ์ฝ๊ธฐ์ ์ข์ง๋ง, ์ปดํจํฐ๊ฐ groupbyยท์ง๊ณยท์๊ฐํ๋ฅผ ํ๋ ค๋ฉด ๋งค๋ฒ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๊ฐ์ ๋ฏ์ด๋ด์ผ ํฉ๋๋ค. Tidy Data๋ ๊ทธ ๋ฏ์ด๋ด๊ธฐ๋ฅผ ๋จ ํ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ํต์ผํด ์ค๋๋ค.
์์ ์ ์ด๋ ๊ฒ ์ ์ํฉ๋๋ค. ์์ปด์ Tidy Data๋ฅผ ์ธ ๊ฐ์ง ์์น์ผ๋ก ๋ชป๋ฐ์ต๋๋ค โ ๊ฐ ๋ณ์๋ ํ๋์ ์ด์, ๊ฐ ๊ด์ธก์น๋ ํ๋์ ํ์, ๊ฐ ๊ด์ธก ๋จ์ ์ ํ์ ํ๋์ ํ๋ฅผ ์ด๋ฃฌ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๊ฑด ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ 3์ ๊ทํ(3rd normal form)์ ํต๊ณ ์ธ์ด๋ก ๋ฐ๊ฟ ์ด ๊ฒ์ด๋ผ๊ณ ๋ฐํ๋๋ค. (์ถ์ฒ: Hadley Wickham, Tidy Data, Journal of Statistical Software, ยง2.3)
# Tidy Data 3์์น (Wickham, 2014) # 1. Each variable forms a column. ๊ฐ ๋ณ์ = ํ๋์ ์ด # 2. Each observation forms a row. ๊ฐ ๊ด์ธก์น = ํ๋์ ํ # 3. Each observational unit forms a table. ๊ฐ ๊ด์ธก ๋จ์ = ํ๋์ ํ
์ฌ๊ธฐ์ ๊ฐ์์ "long form"์ด ์ฐ๊ฒฐ๋ฉ๋๋ค. ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ด์๋ wide form(๋์ ํํ: ๋ณ์๋ฅผ ์์ผ๋ก ํผ์ณ ์ด์ ๋๋ฆผ)๊ณผ long form(๊ธด ํํ: ๋ณ์๋ฅผ ์๋๋ก ์์ ํ์ ๋๋ฆผ)์ด ์๋๋ฐ, tidy๋ ๋ณดํต long form ์ชฝ์ ๋๋ค. ์๋ ํ๊ฐ ๊ฐ์ ์ ๋ณด์ ๋ ์ผ๊ตด์ ๋๋ค.
| ๊ตฌ๋ถ | wide form (๋์/์ง์ ๋ถํ๊ธฐ ์ฌ์) | long form (๊ธด/tidy) |
|---|---|---|
| ๋ชจ์ | ์ด์ด ์์ผ๋ก ๋ง์ | ํ์ด ์๋๋ก ๋ง์ |
| ์ | religion, <$10k, $10-20k, โฆ | religion, income, frequency |
| ์ฌ๋ ๋ | ์ฝ๊ธฐ ์ข์(์์ฝํ) | ์ฅํฉํด ๋ณด์ |
| ๊ธฐ๊ณ ์ฒ๋ฆฌ | ๋งค๋ฒ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋ฏ์ด์ผ ํจ | groupbyยท์ง๊ณยทํ๋กฏ ํ ๋ฐฉ์ |
seabornยทplotly ๊ฐ์ ์๊ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, groupby().agg() ์ง๊ณ, ๊ทธ๋ฆฌ๊ณ ML ๋ชจ๋ธ์ ์
๋ ฅ X ๋ง๋ค๊ธฐ ์ ๋จ๊ณ๊ฐ ์ ๋ถ tidy(long) ํํ๋ฅผ ์ ์ ๋ก ๋์๊ฐ๋๋ค. "๋ฐ์ดํฐ๊ฐ ์ ์ด๋ ๊ฒ ํ๋กฏ์ด ์ ๊ทธ๋ ค์ง์ง?" ํ๋ฐ์ ํํ๊ฐ wide๋ผ์์
๋๋ค.โ ๊ทธ๋ผ ์ค๋ฌด ๋ฐ์ดํฐ๋ ์ด 3์์น์ ์ด๋ป๊ฒ ์ด๊ธธ๊น์? ์์ปด์ด ์ ๋ฆฌํ 5๋ ์ ํ์ ๋ด ๋๋ค.
3. ์ง์ ๋ถํ ๋ฐ์ดํฐ 5๋ ์ ํ
์์ปด์ "ํ๋ณตํ ๊ฐ์ ์ ๋ค ๋น์ทํ์ง๋ง ๋ถํํ ๊ฐ์ ์ ์ ๋ง๋ค์ ๋ฐฉ์์ผ๋ก ๋ถํํ๋ค"๋ ํจ์คํ ์ด๋ฅผ ๋น๋ ค, ์ง์ ๋ถํ ๋ฐ์ดํฐ๋ ์ ๋ง๋ค์ ๋ฐฉ์์ผ๋ก ์ง์ ๋ถํ๋ค๊ณ ๋งํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ค์ ๋ก ๊ฒช์ด๋ณด๋ฉด ๋๋ถ๋ถ ์๋ ๋ค์ฏ ๊ฐ์ง๋ก ์๋ ดํ๋๋ผ๋ ๊ฒ ๋ ผ๋ฌธ์ ๊ด์ฐฐ์ ๋๋ค. (์ถ์ฒ: Tidy Data, ยง3)
ํ ๋จ์ ์ฌ๋ฌ ํโ ์ ๊ทํ
โฃ(ํ ํ์ ์ฌ๋ฌ ์ข ๋ฅ์ ๊ด์ธก ๋จ์๊ฐ ์์)์ โค(ํ ๊ด์ธก ๋จ์๊ฐ ์ฌ๋ฌ ํ์ ์ชผ๊ฐ์ง)๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทํยท์กฐ์ธ์ ์์ญ์ด๋ผ ๋ณ๋ ๊ธ๋ก ๋ฏธ๋ฃจ๊ณ , ์ฌ๊ธฐ์๋ ํ ํ๋๋ฅผ ์ฌ๊ตฌ์กฐํํ๋ โ โกโข์ ์ง์คํ๊ฒ ์ต๋๋ค.
โ ์ฒซ ๋ฒ์งธ, ๊ฐ์ฅ ํํ "์ด ์ด๋ฆ์ด ์ฌ์ค์ ๊ฐ"์ธ ๊ฒฝ์ฐ์ ๋๋ค.
4. melt โ ์ด ์ด๋ฆ์ด ๊ฐ์ผ ๋ (์ข ๊ตยท์๋ ๋ฐ์ดํฐ)
์์ ์ง๋์์ โ ๋ฒ ์นธ์
๋๋ค. ๋ฐํ์ฉ์ผ๋ก ์์๊ฒ ๋ง๋ ํ๋ ๋๊ฐ ์ด ํจ์ ์ ๋น ์ ธ ์์ต๋๋ค. ๊ฐ์ ๋ฐ์ดํฐ ์ค pew.txt(๋ฏธ๊ตญ Pew Research Center์ ์ข
๊ต๋ณ ์๋ ์กฐ์ฌ)๊ฐ ๋ฑ ๊ทธ๋ ์ต๋๋ค.
melt (๋
น์ด๋ค): ์์ผ๋ก ํผ์ณ์ง ์ฌ๋ฌ ์ด์ ๋ ๊ฐ์ ์ด(์ด๋ฆ ์ด + ๊ฐ ์ด)๋ก ๋
น์ฌ ์๋๋ก ์๋ ๋ณํ โ wide๋ฅผ long์ผ๋ก ๋ฐ๊พธ๋ ๋ํ ๋๊ตฌ.
์ง๊ด์ ์ผ๋ก๋ ์ผ์ ํ(๊ฐ ์นธ=์ด)์ ๋ น์ฌ์ ํ ์ค๊ธฐ ๋ฌผ(์ด๋ฆยท๊ฐ ๋ ์ด)๋ก ํ๋ ค๋ณด๋ด๋ ๊ทธ๋ฆผ์ ๋๋ค. ๊ทธ๋์ ์ด๋ฆ์ด "melt"์์.
๊ณต์ ๋ฌธ์๋ ์ด๋ ๊ฒ ์ค๋ช
ํฉ๋๋ค. pandas ๊ณต์ ๋ ํผ๋ฐ์ค๋ melt๋ฅผ "id_vars๋ก ์ง์ ํ ์๋ณ ์ด์ ๊ทธ๋๋ก ๋๊ณ , ๋๋จธ์ง ์ด๋ค์ variable/value ๋ ์ด๋ก unpivotํ๋ค"๊ณ ์ ์ํฉ๋๋ค. ์๋๊ฐ ๊ณต์ ๋ฌธ์์ ์ค๋ฆฐ ์์ ๊ทธ๋๋ก์
๋๋ค. (์ถ์ฒ: pandas.melt)
# pandas ๊ณต์ ๋ฌธ์ ์์ โ ๋์กฐ ๊ธฐ์ค df = pd.DataFrame({"A":["a","b","c"], "B":[1,3,5], "C":[2,4,6]}) pd.melt(df, id_vars=["A"], value_vars=["B","C"]) # A variable value # 0 a B 1 โ B,C ์ด ์ด๋ฆ์ด 'variable' ์ด์ ๊ฐ์ผ๋ก ๋ด๋ ค์ค๊ณ # 1 b B 3 ์๋ ์ ๊ฐ์ 'value' ์ด๋ก ์์ธ๋ค # ... # 3 a C 2
๊ฐ์ ๋ฐ์ดํฐ์ ์ ์ฉํ๋ฉด ์ด๋ ๊ฒ ๋ฉ๋๋ค. pew.txt๋ religion ํ ์ด์ ๋นผ๋ฉด ๋๋จธ์ง ์ด ์ด๋ฆ(<$10k, $10-20kโฆ)์ด ์ ๋ถ "์๋ ๊ตฌ๊ฐ"์ด๋ผ๋ ๋ณ์์ ๊ฐ์
๋๋ค. ์ฆ ์ด ์ด๋ฆ ์๋ฆฌ์ ๋ฐ์ดํฐ๊ฐ ์์ ์๋ ๊ฑฐ์ฃ . religion๋ง ์๋ณ ์ด๋ก ๊ณ ์ ํ๊ณ meltํ๋ฉด tidyํด์ง๋๋ค.
pew = pd.read_csv("pew.txt", sep="\t") print(pew.shape) # (18, 11) โ 18๊ฐ ์ข ๊ต ร 10๊ฐ ์๋์ด + religion tidy = pew.melt(id_vars="religion", # ๋๋จธ์ง ์๋ ์ด ์ ๋ถ๋ฅผ ๋ น์ธ๋ค var_name="income", value_name="frequency") print(tidy.shape) # (180, 3) โ 18 ร 10 = 180 ํ์ผ๋ก ๊ธธ์ด์ง print(tidy.head(4)) # religion income frequency # 0 Agnostic <$10k 27 # 1 Atheist <$10k 12 # 2 Buddhist <$10k 27 # 3 Catholic <$10k 418
์ฌ๋ฐ๋ ์ง์ ํ๋. ์์ปด์ R ์ฌ๋์ด๋ผ ๋
ผ๋ฌธ์์๋ reshape2::melt๋ฅผ ์ผ๊ณ , ์ง๊ธ ๊ทธ์ ํ์ ๋๊ตฌ tidyr์์๋ pivot_longer()๊ฐ ์ด ์ญํ ์ ํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ tidyr ๊ณต์ ๋ฌธ์์ ๋ํ ์์ ๊ฐ ๋ฐ๋ก ์ด Pew ๋ฐ์ดํฐ(relig_income)์์. ๊ฐ์ ๋ฐ์ดํฐ์ ๊ณต์ ๋ฌธ์ ์์ ๊ฐ ๊ฐ์ ๋ฐ์ดํฐ์ธ ์
์
๋๋ค. (์ถ์ฒ: tidyr::pivot_longer)
# R tidyr ๊ณต์ ์์ โ ์์ ํ ๊ฐ์ ๋ฐ์ดํฐ, ๊ฐ์ ๊ฒฐ๊ณผ (18ร11 โ 180ร3) relig_income |> pivot_longer(!religion, names_to = "income", values_to = "count")
| ํญ๋ชฉ | pandas ๊ณต์ ์์ | ๊ฐ์ ๋ฐ์ดํฐ(pew) ์ ์ฉ | ์ฐจ์ด ยท ์ด์ |
|---|---|---|---|
| ์๋ณ ์ด | id_vars=["A"] | id_vars="religion" | ๊ณ ์ ํ ๋ณ์ ์ง์ โ ๊ฐ๋ ๋์ผ |
| ๋ น์ผ ์ด | value_vars=["B","C"] ๋ช
์ | ์๋ต(๋๋จธ์ง ์ ๋ถ ์๋) | ์ด์ด 10๊ฐ๋ผ ์ผ์ผ์ด ์ ์ โ ์๋ต ์ id ์ธ ์ ๋ถ |
| ๊ฒฐ๊ณผ ์ด ์ด๋ฆ | variable/value ๊ธฐ๋ณธ๊ฐ | income/frequency | ์๋ฏธ๊ฐ ๋๋ฌ๋๊ฒ ๊ฐ๋ช โ ์ค๋ฌด ๊ถ์ฅ |
| ๋ชจ์ ๋ณํ | 3ํ โ 6ํ | 18ํ โ 180ํ | ๋ ๋ค wideโlong, ์๋ฆฌ ๋์ผ |
var_nameยทvalue_name์ "variable"/"value" ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋์ง ๋ง๊ณ ๊ผญ ์๋ฏธ ์๋ ์ด๋ฆ์ผ๋ก ๋ฐ๊พธ์ธ์. 6๊ฐ์ ๋ค์ ๋ด๊ฐ value๊ฐ ๋น๋์ธ์ง ๊ธ์ก์ธ์ง ๋ชฐ๋ผ ํค๋งต๋๋ค.โ melt๋ก ํ๋๋ ์ด๋ฒ์ ์ด ์ด๋ฆ ํ๋์ ๋ณ์๊ฐ ๋ ๊ฐ ๋ญ์ณ ์๋ ๊ฒฝ์ฐ๊ฐ ๋์ต๋๋ค.
5. split โ ํ ์ด์ ๋ณ์๊ฐ ๋ ๊ฐ์ผ ๋ (๊ฒฐํต ๋ฐ์ดํฐ)
์ง๋์ โก๋ฒ ์นธ์
๋๋ค. melt๋ฅผ ํ๊ณ ๋๋ฉด variable ์ด์ ๊ฐ์ด ์ฌ์ค์ ์ฌ๋ฌ ์ ๋ณด๋ฅผ ํฉ์ณ ๋์ ์ฝ๋์ธ ๊ฒฝ์ฐ๊ฐ ์ฆ์ต๋๋ค. ๊ฐ์์ tb.csv(WHO ๊ฒฐํต ํต๊ณ)๊ฐ ์ ํ์ ์ด์์. ์ด ์ด๋ฆ์ด new_sp_m1524 ๊ฐ์๋ฐ, ์ฌ๊ธฐ์ ์ฑ๋ณ(m)๊ณผ ๋์ด๋(15โ24์ธ)๊ฐ ํ ๋ฉ์ด๋ฆฌ๋ก ๋ถ์ด ์์ต๋๋ค.
split (์ชผ๊ฐ๋ค): ํ ๋ฌธ์์ด์ ๊ตฌ๋ถ ๊ท์น์ ๋ฐ๋ผ ์ฌ๋ฌ ์กฐ๊ฐ์ผ๋ก ๋๋๋ ๊ฒ โ ์ฌ๊ธฐ์๋ ๋ญ์น ์ด ์ด๋ฆ์ ์ฑ๋ณยท๋์ด๋ ๋ ๋ณ์๋ก ๋ถ๋ฆฌ.
tidy ์์น 1๋ฒ("ํ ๋ณ์ = ํ ์ด")์ ์ ๋ฉด์ผ๋ก ์ด๊ธด ์ํ๋ผ, ๋ถ์ด ์๋ ๊ฑธ ๋ผ์ด ๊ฐ์ ์ด์ ์ฃผ๋ ๊ฒ ๋ชฉํ์ ๋๋ค.
๊ณต์ ๋ฌธ์๋ ์ด๋ ๊ฒ ์ค๋ช
ํฉ๋๋ค. pandas Series.str.split์ expand=True๋ฅผ ์ฃผ๋ฉด ์ชผ๊ฐ ์กฐ๊ฐ๋ค์ด ์ฌ๋ฌ ๊ฐ์ ์ด๋ก ํผ์ณ์ง๋ค๊ณ ๋ช
์ํฉ๋๋ค(๋ถ์กฑํ ์นธ์ NaN์ผ๋ก ์ฑ์). (์ถ์ฒ: pandas.Series.str.split)
# pandas ๊ณต์ ๋ฌธ์ ์์ โ expand=True ๋ ์กฐ๊ฐ์ '์ด'๋ก ํผ์น๋ค s = pd.Series(["this is a regular sentence", ...]) s.str.split(expand=True) # 0 1 2 3 4 # 0 this is a regular sentence โ ๊ณต๋ฐฑ ๊ธฐ์ค์ผ๋ก 5๊ฐ ์ด ์์ฑ
๊ฐ์ ๋ฐ์ดํฐ์ ์ ์ฉํ๋ฉด ์ด๋ ๊ฒ ๋ฉ๋๋ค. tb.csv๋ ๊ตฌ๋ถ์๊ฐ ์์ด m1524์ฒ๋ผ ๋ฑ ๋ถ์ด ์์ด์, ๊ณต์ ์์ ์ฒ๋ผ ๊ตฌ๋ถ์ split์ด ์๋๋ผ ์์น ๊ธฐ๋ฐ ์ฌ๋ผ์ด์ฑ์ด ๋ ๊น๋ํฉ๋๋ค(์ฒซ ๊ธ์=์ฑ๋ณ, ๋๋จธ์ง=๋์ด๋). ๋๊ตฌ๋ .str[...]๋ก ์ด์ง ๋ฐ๊พธ๋, "ํ ์ด์ ๋ญ์น ์ ๋ณด๋ฅผ ๋ ๋ณ์๋ก ๋ผ์ด๋ธ๋ค"๋ ๋ชฉ์ ์ ๊ณต์ ์์ ์ ์์ ํ ๊ฐ์ต๋๋ค.
tb = pd.read_csv("tb.csv") m = tb.melt(id_vars=["iso2","year"], var_name="column", value_name="cases").dropna() # "new_sp_m1524" โ ์ ๋์ด ๋ผ๊ณ โ ์ฒซ ๊ธ์=์ฑ๋ณ, ๋๋จธ์ง=๋์ด๋ demo = m["column"].str.replace("new_sp_", "", regex=False) m["sex"] = demo.str[0] # 'm' ๋๋ 'f' m["age"] = demo.str[1:] # '1524', '014' โฆ print(m[["iso2","year","sex","age","cases"]].head(3)) # iso2 year sex age cases # AE 1999 m 014 4.0 โ ์ฑ๋ณยท๋์ด๋๊ฐ ๊ฐ์์ ์ด๋ก ๋ถ๋ฆฌ๋จ # AE 2000 m 014 2.0 # AE 2002 m 014 1.0
R ์ชฝ์์๋ ์์ปด์ด ์ด๊ฑธ ์์ ํ ํจ์๋ก ํก์ํ์ต๋๋ค. pivot_longer(names_to = c("sex","age"), names_sep = ...)์ฒ๋ผ ํด๋ฉด์ ๋์์ ์ชผ๊ฐ๊ธฐ๊ฐ ๋ฉ๋๋ค โ melt์ split์ด ํ ์คํ
์ผ๋ก ํฉ์ณ์ง ์
์
๋๋ค. (์ถ์ฒ: tidyr::pivot_longer, names_sep/names_to)
| ํญ๋ชฉ | pandas ๊ณต์ ์์ | ๊ฐ์ ๋ฐ์ดํฐ(tb) ์ ์ฉ | ์ฐจ์ด ยท ์ด์ |
|---|---|---|---|
| ๋๊ตฌ | str.split(expand=True) | str[0] / str[1:] ์ฌ๋ผ์ด์ฑ | ๊ตฌ๋ถ์๊ฐ ์์ด ์์น๋ก ์๋ฆ โ ๋ชฉ์ ์ ๋์ผ |
| ๊ฒฐ๊ณผ | ๊ณต๋ฐฑ๋ง๋ค N๊ฐ ์ด | sex, age 2๊ฐ ์ด | ์ชผ๊ฐ ๋ค ๊ฐ์ ์ด์ด ๋๋ ์๋ฆฌ ๊ฐ์ |
| ์ ํ ๋จ๊ณ | ์์ | ๋จผ์ melt ํ์ | ์ด ์ด๋ฆ์ด ๊ฐ์ด์ ๋ญ์น ๋ณ์ โ โ +โก ๋ณตํฉ |
0์ธ ์นธ๊ณผ ๊ฒฐ์ธก(NaN)์ธ ์นธ์ด ์์ฌ ์์ต๋๋ค. ๋
ผ๋ฌธ๋ ์ง๋ฏ ์ด ๋์ ์๋ฏธ๊ฐ ๋ฌ๋ผ์("0๊ฑด ํ์ธ"๊ณผ "์กฐ์ฌ ์ ํจ"). dropna()๋ก ๊ฒฐ์ธก๋ง ๊ฑธ๋ฌ๋ด๋, 0์ ๊ฒฐ์ธก๊ณผ ๋ญ๋ฑ๊ทธ๋ ค ์ง์ฐ์ง ์๋๋ก ์ฃผ์ํ์ธ์.2026-08-31_seoul_temp์ฒ๋ผ ๋ ์งยท์ง์ญยท์ธก์ ๊ฐ์ด ํ ์ปฌ๋ผ๋ช
/๋ฌธ์์ด์ ๋ญ์ณ ์ค๋ ๊ฒฝ์ฐ๊ฐ ํํฉ๋๋ค. ์ด๊ฑธ ๊ฐ ๋ณ์ ์ด๋ก ๋ผ์ด์ผ ํํฐยท์ง๊ณ๊ฐ ๋ฉ๋๋ค.โ ๋ง์ง๋ง, ๋ณ์๊ฐ ํ์๋ ์๊ณ ์ด์๋ ์์ด melt๋ง์ผ๋ก๋ ์ ๋๋ ๊ฐ์ฅ ๊น๋ค๋ก์ด ๊ฒฝ์ฐ์ ๋๋ค.
6. pivot โ ๊ฐ์ด ํยท์ด ์์ชฝ์ ํฉ์ด์ก์ ๋ (๊ธฐ์ ๋ฐ์ดํฐ)
์ง๋์ โข๋ฒ, ๋
ผ๋ฌธ์ด "๊ฐ์ฅ ๋ณต์กํ ํํ"๋ผ๊ณ ๋ถ๋ฅธ ์ ํ์
๋๋ค. ๊ฐ์์ weather.txt(๋ฉ์์ฝ ๊ธฐ์๊ด์ธก์ ์ผ๋ณ ๊ธฐ์จ)๊ฐ ๊ทธ๋ ์ต๋๋ค. ๋ ์ง๋ d1~d31 ์ด๋ก ๊ฐ๋ก๋ก ํฉ์ด์ ธ ์๊ณ , ๋์์ ์ต๊ณ ๊ธฐ์จยท์ต์ ๊ธฐ์จ์ด element ์ด ์์์ TMAX/TMIN ํ์ผ๋ก ํฉ์ด์ ธ ์์ด์. ํ ๋ฐฉํฅ์ melt๋ก ํด์ผ ํ๊ณ , ๋ค๋ฅธ ๋ฐฉํฅ์ ๋ค์ ์ ์ด์ผ ํฉ๋๋ค.
pivot (๋๋ ค ์ธ์ฐ๋ค / cast): ํ ์ด์ ์์ฌ ์๋ ๊ฐ์ ์ฌ๋ฌ ์ด๋ก ํผ์ณ ์ธ์ฐ๋ ๋ณํ โ melt์ ๋ฐ๋. ๋
ผ๋ฌธ ์ฉ์ด๋ก๋ cast.
melt๊ฐ ์ด์ ๋
น์ฌ ์๋๋ก ์์๋ค๋ฉด, pivot์ ๊ทธ ์์ธ ๊ฑธ ๋ค์ ์์ผ๋ก ์ธ์๋๋ค. ์ฌ๊ธฐ์๋ TMAX/TMIN์ด ์๋ก ๋ค๋ฅธ ๋ณ์์ธ๋ฐ ํ ์ด์ ๊ฐ์ผ๋ก ๋๋ ค ์์ผ๋, ๊ฐ์์ ์ด๋ก ์ธ์ ์ฌ๋ ค์ผ tidyํด์ง๋๋ค.
๊ณต์ ๋ฌธ์๋ ์ด๋ ๊ฒ ์ค๋ช
ํฉ๋๋ค. pandas pivot_table์ index(ํ์ด ๋ ํค), columns(์ด๋ก ํผ์น ํค), values(์นธ์ ์ฑ์ธ ๊ฐ)๋ก long ๋ฐ์ดํฐ๋ฅผ wide๋ก ์ฌ๊ตฌ์ฑํ๋ค๊ณ ์ ์ํฉ๋๋ค. (์ถ์ฒ: pandas.pivot_table)
# pandas ๊ณต์ ๋ฌธ์ ์์ โ long์ index/columns/values๋ก ์ธ์ด๋ค pd.pivot_table(df, values=["D","E"], index=["A","C"], aggfunc="mean") # A,C ์กฐํฉ์ด ํ์ด ๋๊ณ , DยทE๊ฐ ์ด์ด ๋์ด ๊ฐ์ด ์ฑ์์ง
๊ฐ์ ๋ฐ์ดํฐ์ ์ ์ฉํ๋ฉด ์ด๋ ๊ฒ ๋ฉ๋๋ค. 2๋จ๊ณ์์. โ ๋จผ์ d1~d31์ melt๋ก ํด์ day ์ด์ ๋ง๋ค๊ณ (๊ฐ๋ก ํฉ์ด์ง ํด๊ฒฐ), โก ๊ทธ๋ค์ element(TMAX/TMIN)๋ฅผ pivot์ผ๋ก ์ธ์ ๋ ๊ฐ์ ์ด๋ก ์ฌ๋ฆฝ๋๋ค(์ธ๋ก ํฉ์ด์ง ํด๊ฒฐ).
w = pd.read_csv("weather.txt", sep="\t") # โ melt: ๋ ์ง ์ด d1~d31 ์ 'day' ํ ์ด๋ก ํธ๋ค long = w.melt(id_vars=["id","year","month","element"], var_name="day", value_name="value").dropna() long["day"] = long["day"].str.replace("d","",regex=False).astype(int) # ์ด ์์ : element(TMAX/TMIN)๊ฐ ์์ง '๊ฐ'์ผ๋ก ํ ์ด์ ๋๋ ค ์์ โ ์์ง ์ tidy # โก pivot: element ๋ฅผ ์ด๋ก ์ธ์ด๋ค (TMAX, TMIN ๊ฐ์ ์ด๋ก) wide = long.pivot_table(index=["id","year","month","day"], columns="element", values="value").reset_index() print(wide.head(3)) # id year month day TMAX TMIN # MX000017004 2010 1 30 278.0 145.0 โ ํ๋ฃจ = ํ ํ, ๊ธฐ์จ = ๊ฐ ์ด # MX000017004 2010 2 2 273.0 144.0 (๊ฐ์ 0.1โ ๋จ์: 278 = 27.8โ) # MX000017004 2010 2 3 241.0 144.0
ํฌ์ธํธ๋ element ์ด์ด ๋ณ์๊ฐ ์๋๋ผ ๋ณ์ ์ด๋ฆ์ ๋ด์ ์ด์ด์๋ค๋ ๊ฒ๋๋ค. TMAX์ TMIN์ ์๋ก ๋ค๋ฅธ ์ธก์ ๋์ด๋ ๊ฐ์ ์ด์ด์ด์ผ ๋ง์ต๋๋ค. pivot์ผ๋ก ์ธ์ฐ๊ณ ๋๋ฉด "ํ๋ฃจ = ํ ํ, ์ต๊ณ ยท์ต์ ๊ธฐ์จ = ๊ฐ ์ด"์ด๋ผ๋ tidy ํํ๊ฐ ์์ฑ๋ฉ๋๋ค. R์์๋ ์ด ๋ฐ๋ ๋ฐฉํฅ์ pivot_wider()๊ฐ ๋งก์ต๋๋ค.
| ํญ๋ชฉ | pandas ๊ณต์ ์์ | ๊ฐ์ ๋ฐ์ดํฐ(weather) ์ ์ฉ | ์ฐจ์ด ยท ์ด์ |
|---|---|---|---|
| ํ ํค | index=["A","C"] | index=[id,year,month,day] | ๊ด์ธก์น๋ฅผ ์๋ณํ๋ ํค โ ๊ฐ๋ ๋์ผ |
| ํผ์น ํค | columns ๋ฏธ์ฌ์ฉ(์์ ) | columns="element" | TMAX/TMIN์ ์ด๋ก ์ธ์ฐ๋ ๊ฒ ํต์ฌ |
| ์ ํ ๋จ๊ณ | ์์ | melt ๋จผ์ | ๊ฐ๋กยท์ธ๋ก ์์ชฝ ํฉ์ด์ง์ด๋ผ melt+pivot 2๋จ |
| ๋ฐฉํฅ | long โ wide | long โ wide | melt์ ์ญ์ฐ์ฐ โ ์๋ฆฌ ๋์ผ |
pivot vs pivot_table: ๊ฐ์ (ํ,์ด) ์กฐํฉ์ ๊ฐ์ด ํ๋๋ฟ์ด๋ฉด pivot, ์ฌ๋ฌ ๊ฐ๋ผ ์ง๊ณ(meanยทsum ๋ฑ)๊ฐ ํ์ํ๋ฉด pivot_table์ ์๋๋ค. ์ค๋ณต ํค์์ pivot์ ์๋ฌ, pivot_table์ aggfunc๋ก ๋ญ๊ฐญ๋๋ค.pivot์ผ๋ก ์ ๊น wide๋ก ์ธ์๋๋ค.โ ์ธ ๋๊ตฌ๋ฅผ ํ ์ฅ์ผ๋ก ์ ๋ฆฌํ๊ณ ๋ซ๊ฒ ์ต๋๋ค.
์นํธ์ํธ & ํต์ฌ 4๊ฐ์ง
| ์ฆ์ (์ง์ ๋ถํ ์ ํ) | ๋๊ตฌ | pandas ํจํด |
|---|---|---|
| ์ด ์ด๋ฆ์ด ์ฌ์ค์ ๊ฐ (wide) | melt | df.melt(id_vars=..., var_name=..., value_name=...) |
| ํ ์ด์ ๋ณ์๊ฐ ์ฌ๋ฌ ๊ฐ | split | s.str.split(sep, expand=True) / s.str[a:b] |
| ๊ฐ์ด ํยท์ด ์์ชฝ์ ํฉ์ด์ง | pivot | df.pivot_table(index=..., columns=..., values=...) |
| (์ฐธ๊ณ ) longโwide ๋๋๋ฆฌ๊ธฐ | pivot | melt์ ์ญ์ฐ์ฐ โ R์ pivot_wider() |
1. Tidy = ๋ณ์๋ ์ด, ๊ด์ธก์น๋ ํ, ๊ด์ธก๋จ์๋ ํ (Wickham 3์์น = DB ์ 3์ ๊ทํ).
2. ์ฌ๋์ด ๋ณด๊ธฐ ์ข์ ํ(wide)์ ๊ธฐ๊ณ๊ฐ ๋ค๋ฃจ๊ธฐ ์ข์ ํ(long)๋ ๋ค๋ฅด๋ค โ ๋ถ์์ long์ด ๊ธฐ๋ณธ.
3. ์ง์ ๋ถํจ์ ๋๋ถ๋ถ ์ด์ด๋ฆ=๊ฐ / ํ ์ด์ ์ฌ๋ฌ ๋ณ์ / ํยท์ด ์์ชฝ ํฉ์ด์ง์ผ๋ก ์๋ ดํ๋ค.
4. ๊ณ ์น๋ ๋๊ตฌ๋ melt(ํด๊ธฐ) ยท split(์ชผ๊ฐ๊ธฐ) ยท pivot(์ธ์ฐ๊ธฐ) ์ ์ด๋ฉด ์ถฉ๋ถํ๋ค.
์ฐธ๊ณ ์๋ฃ
- Hadley Wickham, Tidy Data โ Journal of Statistical Software (tidy 3์์น ยง2.3, 5๋ ์ ํ ยง3์ ์์ )
- pandas ๊ณต์ ๋ฌธ์ โ melt ยท pivot_table ยท Series.str.split
- tidyr(R, Wickham) ๊ณต์ ๋ฌธ์ โ pivot_longer (melt/split์ ํ์,
relig_income์์ ) - ๋ฐ์ดํฐ ์ถ์ฒ: ๊ฐ์ ์ฒจ๋ถ
tidy-data-master/data(pewยทtbยทweather โ WHOยทPewยทGHCN ์์๋ฃ)