DataFrame高階操作:如何進(jìn)行行列轉(zhuǎn)換

轉(zhuǎn)自 Pandas與數(shù)據(jù)整理

在 Tidy Data 論文中羔飞,Wickham 博士 提出了這樣一種“整潔”的數(shù)據(jù)結(jié)構(gòu):每個變量是一列,每次觀測結(jié)果是一行,不同的觀測類型存放在單獨的表中。他認(rèn)為這樣的數(shù)據(jù)結(jié)構(gòu)可以幫助分析師更簡單高效地進(jìn)行處理溉躲、建模、和可視化益兄。他在論文中列舉了 五種 不符合整潔數(shù)據(jù)的情況锻梳,并演示了如何通過 R 語言 對它們進(jìn)行整理。本文中净捅,我們將使用 Python 和 Pandas 來達(dá)到同樣的目的疑枯。

文中的源代碼和演示數(shù)據(jù)可以在 GitHub(鏈接)上找到。讀者應(yīng)該已經(jīng)安裝好 Python 開發(fā)環(huán)境蛔六,推薦各位使用 Anaconda 和 Spyder IDE荆永。

列名稱是數(shù)據(jù)值,而非變量名

import pandas as pd
df = pd.read_csv('data/pew.csv')
df.head(10)
1.png

表中的列“<10k”国章、“10-20k”其實是“收入”變量的具體值具钥。變量 是指某一特性的觀測值,如身高捉腥、體重氓拼,本例中則是收入、宗教信仰抵碟。表中的數(shù)值數(shù)據(jù)構(gòu)成了另一個變量——人數(shù)。要做到 每個變量是一列 坏匪,我們需要進(jìn)行以下變換:

df = df.set_index('religion')
df = df.stack()
df.index = df.index.rename('income', level=1)
df.name = 'frequency'
df = df.reset_index()
df.head(10)
2.png

這里我們使用了 Pandas 多級索引的 stack / unstack 特性拟逮。stack() 會將列名轉(zhuǎn)置為新一級的索引,并將數(shù)據(jù)框(DataFrame)轉(zhuǎn)換成序列(Series)适滓。轉(zhuǎn)置后敦迄,我們對行和列的名稱做一些調(diào)整,再用 reset_index() 將數(shù)據(jù)框還原成普通的二維表凭迹。

除了使用多級索引罚屋,Pandas 還提供了另一種更為便捷的方法——melt()。該方法接收以下參數(shù):

  • frame: 需要處理的數(shù)據(jù)框嗅绸;
  • id_vars: 保持原樣的數(shù)據(jù)列脾猛;
  • value_vars: 需要被轉(zhuǎn)換成變量值的數(shù)據(jù)列;
  • var_name: 轉(zhuǎn)換后變量的列名鱼鸠;
  • value_name: 數(shù)值變量的列名猛拴。
df = pd.read_csv('data/pew.csv')
df = pd.melt(df, id_vars=['religion'], value_vars=list(df.columns)[1:],
             var_name='income', value_name='frequency')
df = df.sort_values(by='religion')
df.to_csv('data/pew-tidy.csv', index=False)
df.head(10)

這段代碼會輸出相同的結(jié)果羹铅,下面的示例中我們都將使用 melt() 方法。我們再來看另外一個案例:

3.png

在這個數(shù)據(jù)集中愉昆,每周的排名都被記錄到了不同的數(shù)據(jù)列中职员。如果我們想要回答“Dancing Queen 這首歌在 2000年7月15日 的排名如何”,就需要結(jié)合 date.entered 字段做一些運算才行跛溉。下面我們來對這份數(shù)據(jù)進(jìn)行整理:

df = pd.read_csv('data/billboard.csv')
df = pd.melt(df, id_vars=list(df.columns)[:5], value_vars=list(df.columns)[5:],
             var_name='week', value_name='rank')
df['week'] = df['week'].str[2:].astype(int)
df['date.entered'] = pd.to_datetime(df['date.entered']) + pd.to_timedelta((df['week'] - 1) * 7, 'd')
df = df.rename(columns={'date.entered': 'date'})
df = df.sort_values(by=['track', 'date'])
df.to_csv('data/billboard-intermediate.csv', index=False)
df.head(10)
4.png

上述代碼中焊切,我們還將 date.entered 轉(zhuǎn)換成了每一周的具體日期,week 字段也作為單獨的數(shù)據(jù)列進(jìn)行存儲芳室。但是专肪,我們會在表中看到很多重復(fù)的信息,如歌手渤愁、曲名等牵祟,我們將在第四節(jié)解決這個問題。

一列包含多個變量

人們之所以會將變量值作為列名抖格,一方面是這樣的表示方法更為緊湊诺苹、可以在一頁中顯示更多信息,還有一點是這種格式便于做交叉驗證等數(shù)據(jù)分析工作雹拄。下面的數(shù)據(jù)集更是將性別和年齡這兩個變量都放入了列名中:

5.png

m 表示男性(Male)收奔,f 表示女性(Female)怒详,0-14续挟、15-24 則表示年齡段。進(jìn)行數(shù)據(jù)整理時挠轴,我們先用 Pandas 的字符串處理功能截取 sex 字段势篡,再對剩余表示年齡段的子串做映射處理翩肌。

df = pd.read_csv('data/tb.csv')
df = pd.melt(df, id_vars=['country', 'year'], value_vars=list(df.columns)[2:],
             var_name='column', value_name='cases')
df = df[df['cases'] != '---']
df['cases'] = df['cases'].astype(int)
df['sex'] = df['column'].str[0]
df['age'] = df['column'].str[1:].map({
    '014': '0-14',
    '1524': '15-24',
    '2534': '25-34',
    '3544': '35-44',
    '4554': '45-54',
    '5564': '55-64',
    '65': '65+'
})
df = df[['country', 'year', 'sex', 'age', 'cases']]
df.to_csv('data/tb-tidy.csv', index=False)
df.head(10)
6.png

變量存儲在行和列中

下表是一個名為 MX17004 的氣象站收集的溫度數(shù)據(jù)〗疲可以看到念祭,日期被放置在列名中,我們可以用 melt 進(jìn)行處理碍侦;tmax 和 tmin 則表示最高溫度和最低溫度粱坤,他們很顯然是兩個不同的變量,用來衡量單個觀測對象的屬性的瓷产,本例中的觀測對象是“天”站玄。因此,我們需要使用 unstack 將其拆分成兩列濒旦。

7.png
df = pd.read_csv('data/weather.csv')
df = pd.melt(df, id_vars=['id', 'year', 'month', 'element'],
             value_vars=list(df.columns)[4:],
             var_name='date', value_name='value')
df['date'] = df['date'].str[1:].astype('int')
df['date'] = df[['year', 'month', 'date']].apply(
    lambda row: '{:4d}-{:02d}-{:02d}'.format(*row),
    axis=1)
df = df.loc[df['value'] != '---', ['id', 'date', 'element', 'value']]
df = df.set_index(['id', 'date', 'element'])
df = df.unstack()
df.columns = list(df.columns.get_level_values('element'))
df = df.reset_index()
df.to_csv('data/weather-tidy.csv', index=False)
df
8.png

同一表中包含多種觀測類型

在處理 Billboard 數(shù)據(jù)集時株旷,我們會看到冗余的曲目信息,這是因為該表實際記錄的是兩種不同的觀測類型——歌曲曲目和周排名疤估。整理時灾常,我們需要先為每首歌曲生成一個唯一標(biāo)識霎冯,即 id,然后拆分到單獨的表中钞瀑。

df = pd.read_csv('data/billboard-intermediate.csv')
df_track = df[['artist', 'track', 'time']].drop_duplicates()
df_track.insert(0, 'id', range(1, len(df_track) + 1))
df = pd.merge(df, df_track, on=['artist', 'track', 'time'])
df = df[['id', 'date', 'rank']]
df_track.to_csv('data/billboard-track.csv', index=False)
df.to_csv('data/billboard-rank.csv', index=False)
print(df_track, '\n\n', df)
9.png
10.png

同一觀測類型分布在不同表中

原始的數(shù)據(jù)集可能會以兩種方式進(jìn)行了拆分沈撞,一種是按照某個變量拆分,如按年拆分為2000年雕什、2001年缠俺,按地理位置拆分為中國、英國贷岸;另一種是按不同的屬性拆分壹士,如一份數(shù)據(jù)是收集溫度的傳感器記錄的,另一份是濕度傳感器偿警,他們記錄的都是每一天的觀測值躏救。對于第一種情況,我們可以編寫一個讀取數(shù)據(jù)的函數(shù)螟蒸,遍歷目錄中的文件盒使,并將文件名作為單獨的列加入數(shù)據(jù)框,最后使用 pd.concat 進(jìn)行合并七嫌;第二種情況則要求數(shù)據(jù)集中的記錄有一個唯一標(biāo)識少办,如日期、身份證號诵原,并通過 pd.merge 將各個數(shù)據(jù)集聯(lián)系起來英妓。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绍赛,隨后出現(xiàn)的幾起案子蔓纠,更是在濱河造成了極大的恐慌,老刑警劉巖吗蚌,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贺纲,死亡現(xiàn)場離奇詭異,居然都是意外死亡褪测,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門潦刃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侮措,“玉大人,你說我怎么就攤上這事乖杠》衷” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵胧洒,是天一觀的道長畏吓。 經(jīng)常有香客問我墨状,道長,這世上最難降的妖魔是什么菲饼? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任肾砂,我火速辦了婚禮,結(jié)果婚禮上宏悦,老公的妹妹穿的比我還像新娘镐确。我一直安慰自己,他們只是感情好饼煞,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布源葫。 她就那樣靜靜地躺著,像睡著了一般砖瞧。 火紅的嫁衣襯著肌膚如雪息堂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天块促,我揣著相機(jī)與錄音荣堰,去河邊找鬼。 笑死褂乍,一個胖子當(dāng)著我的面吹牛持隧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逃片,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼屡拨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了褥实?” 一聲冷哼從身側(cè)響起呀狼,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎损离,沒想到半個月后哥艇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡僻澎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年貌踏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窟勃。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡祖乳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秉氧,到底是詐尸還是另有隱情眷昆,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站亚斋,受9級特大地震影響作媚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帅刊,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一纸泡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厚掷,春花似錦弟灼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抡爹,卻和暖如春掩驱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冬竟。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工欧穴, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泵殴。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓涮帘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笑诅。 傳聞我的和親對象是個殘疾皇子调缨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Python 和 Pandas 數(shù)據(jù)分析教程 原文:Data Analysis with Python and P...
    布客飛龍閱讀 82,645評論 9 224
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,947評論 2 89
  • 你是一灘清泉, 凈化我骯臟的靈魂吆你; 你是一抹亮光弦叶, 讓迷茫的我找到方向; 你是高高在上的天使妇多, 我卻是卑微的生命伤哺;...
    少女心暖暖閱讀 205評論 0 1
  • 2017年9月11日,弟子蔡小敏者祖,種種子第18天立莉。 發(fā)心:我今天不僅是為了我個人而聞思修,更是為了六道輪回一切如母...
    Rubywry閱讀 145評論 0 2
  • 從廣東來到江蘇實習(xí)9個月了七问,是結(jié)束回去桃序,還是該繼續(xù)? 還沒想到以后要做什么,回去還是一片迷茫烂瘫,從頭來? 還是要休先...
    Summe_Chen閱讀 261評論 0 0