DataFrame高阶操作:如何进行行列转换

转自 Pandas与数据整理

在 Tidy Data 论文中,Wickham 博士 提出了这样一种“整洁”的数据结构:每个变量是一列,每次观测结果是一行,不同的观测类型存放在单独的表中。他认为这样的数据结构可以帮助分析师更简单高效地进行处理、建模、和可视化。他在论文中列举了 五种 不符合整洁数据的情况,并演示了如何通过 R 语言 对它们进行整理。本文中,我们将使用 Python 和 Pandas 来达到同样的目的。

文中的源代码和演示数据可以在 GitHub(链接)上找到。读者应该已经安装好 Python 开发环境,推荐各位使用 Anaconda 和 Spyder IDE。

列名称是数据值,而非变量名

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

表中的列“<10k”、“10-20k”其实是“收入”变量的具体值。变量 是指某一特性的观测值,如身高、体重,本例中则是收入、宗教信仰。表中的数值数据构成了另一个变量——人数。要做到 每个变量是一列 ,我们需要进行以下变换:

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() 会将列名转置为新一级的索引,并将数据框(DataFrame)转换成序列(Series)。转置后,我们对行和列的名称做一些调整,再用 reset_index() 将数据框还原成普通的二维表。

除了使用多级索引,Pandas 还提供了另一种更为便捷的方法——melt()。该方法接收以下参数:

  • frame: 需要处理的数据框;
  • id_vars: 保持原样的数据列;
  • value_vars: 需要被转换成变量值的数据列;
  • var_name: 转换后变量的列名;
  • value_name: 数值变量的列名。
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)

这段代码会输出相同的结果,下面的示例中我们都将使用 melt() 方法。我们再来看另外一个案例:

3.png

在这个数据集中,每周的排名都被记录到了不同的数据列中。如果我们想要回答“Dancing Queen 这首歌在 2000年7月15日 的排名如何”,就需要结合 date.entered 字段做一些运算才行。下面我们来对这份数据进行整理:

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 转换成了每一周的具体日期,week 字段也作为单独的数据列进行存储。但是,我们会在表中看到很多重复的信息,如歌手、曲名等,我们将在第四节解决这个问题。

一列包含多个变量

人们之所以会将变量值作为列名,一方面是这样的表示方法更为紧凑、可以在一页中显示更多信息,还有一点是这种格式便于做交叉验证等数据分析工作。下面的数据集更是将性别和年龄这两个变量都放入了列名中:

5.png

m 表示男性(Male),f 表示女性(Female),0-14、15-24 则表示年龄段。进行数据整理时,我们先用 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 的气象站收集的温度数据。可以看到,日期被放置在列名中,我们可以用 melt 进行处理;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 数据集时,我们会看到冗余的曲目信息,这是因为该表实际记录的是两种不同的观测类型——歌曲曲目和周排名。整理时,我们需要先为每首歌曲生成一个唯一标识,即 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

同一观测类型分布在不同表中

原始的数据集可能会以两种方式进行了拆分,一种是按照某个变量拆分,如按年拆分为2000年、2001年,按地理位置拆分为中国、英国;另一种是按不同的属性拆分,如一份数据是收集温度的传感器记录的,另一份是湿度传感器,他们记录的都是每一天的观测值。对于第一种情况,我们可以编写一个读取数据的函数,遍历目录中的文件,并将文件名作为单独的列加入数据框,最后使用 pd.concat 进行合并;第二种情况则要求数据集中的记录有一个唯一标识,如日期、身份证号,并通过 pd.merge 将各个数据集联系起来。

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • Python 和 Pandas 数据分析教程 原文:Data Analysis with Python and P...
    布客飞龙阅读 82,454评论 9 224
  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,862评论 2 89
  • 你是一滩清泉, 净化我肮脏的灵魂; 你是一抹亮光, 让迷茫的我找到方向; 你是高高在上的天使, 我却是卑微的生命;...
    少女心暖暖阅读 204评论 0 1
  • 2017年9月11日,弟子蔡小敏,种种子第18天。 发心:我今天不仅是为了我个人而闻思修,更是为了六道轮回一切如母...
    Rubywry阅读 137评论 0 2
  • 从广东来到江苏实习9个月了,是结束回去,还是该继续? 还没想到以后要做什么,回去还是一片迷茫,从头来? 还是要休先...
    Summe_Chen阅读 257评论 0 0