第32单元 数据重塑
数据标签是pandas对表格数据的主要贡献。所谓数据标签,是指与列和行相关联的数字或文本标签(例如与列对应的列名,以及与行对应的扁平化索引和分层索引)。这样的关联是非常灵活的:为了与其他frame相匹配,可以修改底层的numpy数组的形状(请参阅第22单元的reshape()函数),这种修改可能会使frame的某些行变为列,某些列变为行。例如,如果一个frame的分层索引具有两个级别(比如“Year”和“State”),而要匹配的另一个frame只具有“State”索引,则pandas会将“Year”标签自动转换为列名。在本单元中,你将了解扁平索引、分层索引和重建索引,以及其他重组数据标签的方式。
索引
frame的索引是一组分配给frame行的标签集合。(标签必须属于相同的数据类型,但标签的值不一定是唯一的。)通过可选参数index,可以给DataFrame()构造器提供索引。和series类似,可以通过属性index.values和columns.values访问和更改列名及索引。
alco2009.columns.values ➾ array(['Beer', 'Wine', 'Spirits', 'Total'], dtype=object) alco2009.index.values ➾ array(['Alabama', 'Alaska', 'Arizona', «...»], dtype=object)
frame中的任意一列都可以作为一个索引——函数reset_index()和set_index(column)分别用于删除现有索引(如果存在的话)和建立一个新索引。这两个函数都返回一个新的frame,但是如果提供了可选参数inplace=True,则函数将直接修改frame对象本身:
alco2009.reset_index().set_index("Beer").head() ➾ State Wine Spirits Total ➾ Beer ➾ 1.20 Alabama 0.22 0.58 0 ➾ 1.31 Alaska 0.54 1.16 0 ➾ 1.19 Arizona 0.38 0.74 0 ➾ 1.07 Arkansas 0.17 0.60 0 ➾ 1.05 California 0.55 0.73 0
frame的索引是重要的行访问工具和相关的行标识符。无论使用哪个列作为索引,它必须是有意义的。在上面的例子中,使用的列就没有意义:啤酒消费量是州的属性,但不是标识符。
一旦有了索引,就可以通过行的索引属性ix来访问某一行,这就像是一个用索引标签作为键的行series的字典。frame的列作为每个series的索引:
alco2009.ix["Nebraska"] ➾ Beer 1.46 ➾ Wine 0.20 ➾ Spirits 0.68 ➾ Total 0.00 ➾ Name: Nebraska, dtype: float64
Python运算符in可以检查具有某个标签的行是否存在于frame中:
"Samoa" in alco2009.index ➾ False
函数drop()返回一个删除了一行或若干行(用列表表示)的frame的副本。如果要删除原始frame中的行,请传递可选参数inplace=True。
重建索引
重建索引从现有的frame或series中选择一定的行排列、列排列或者行和列排列,来创建新的frame或series。本质上,它等同于numpy的一个“智能”索引(参见第23单元),只不过如果pandas在原始frame中找不到请求的行或列标签,它将创建一个新的行或列,并用nan填充它(或它们)。
在下面的例子中,我们创建名称以“S”开头的州列表(该列表包含了不是州名的“Samoa”,当然它也不在alco2009 frame中)。然后,除了最后一列(即“Total”列,它没有被正确初始化)之外,再添加名为“Water”的列。最后,从原始的frame中提取所选的行和列。因为有一行和一列不在原始的frame alco 2009中,所以pandas会自动创建它们:
s_states = [state for state in alco2009.index if state[0] == 'S'] + ["Samoa"] drinks = list(alco2009.columns) + ["Water"] nan_alco = alco2009.reindex(s_states, columns=drinks) ➾ Beer Wine Spirits Water ➾ State ➾ South Carolina 1.36 0.24 0.77 NaN ➾ South Dakota 1.53 0.22 0.88 NaN ➾ Samoa NaN NaN NaN NaN
可选参数method的可能值为"ffill"(正向填充)和"bfill"(后向填充),可以用于填补缺失的值。(这仅适用于单调减或单调增的索引。)关于数据插值的更多信息,请参阅第33单元。
分层索引
pandas支持分层(多级)索引和分层(多级)列名。多级索引也称为多重索引。
多级索引由三个列表组成:
级别名称
每个级别可能存在的所有标签
frame或series中每一项的实际值的列表(列表的长度相同,并等于索引中的级别数)
下面的frame包含完整版本的NIAAA数据集,不只是2009年。它具有州名和年份的多级索引,并且按照两个索引进行排序:先按州名排序,后按年份排序。
alco ➾ Beer Wine Spirits ➾ State Year ➾ Alabama 1977 0.99 0.13 0.84 ➾ 1978 0.98 0.12 0.88 ➾ 1979 0.98 0.12 0.84 ➾ 1980 0.96 0.16 0.74 ➾ 1981 1.00 0.19 0.73 ➾ «...» ➾ Wyoming 2005 1.21 0.23 0.97 ➾ 2006 1.47 0.23 1.05 ➾ 2007 1.49 0.23 1.10 ➾ 2008 1.54 0.23 1.12 ➾ 2009 1.45 0.22 1.10
数据转换操作通常会产生分层索引,但你也可以专门构建它们。函数MultiIndex.from_ tuples()使用带有标签的元组和可选的级别名称列表生成多级索引。可以将多级索引附加到现有的frame或series中,或将其作为参数传递给构造函数DataFrame():
multi = pd.MultiIndex.from_tuples(( ("Alabama", 1977), ("Alabama", 1978), ("Alabama", 1979), ..., ("Wyoming", 2009)), names=["State", "Year"]) ➾ MultiIndex(levels=[['Alabama', 'Alaska', «...», 'Wyoming'], ➾ [1977, 1978, 1979, 1980, «...», 2009]], ➾ labels=[[0, 0, 0, 0, 0, 0, 0, 0, «...», 50], ➾ [0, 1, 2, 3, 4, 5, 6, 7, «...», 32]], ➾ names=['State', 'Year']) alco.index = multi
可以使用与扁平化索引相同的多级索引。部分索引(使用几个标签中的某一个)产生一个frame;完整的索引产生一个series。
alco.ix['Wyoming'].head() ➾ Beer Wine Spirits ➾ Year ➾ 1977 1.79 0.21 1.32 ➾ 1978 1.82 0.22 1.36 ➾ 1979 1.86 0.22 1.30 ➾ 1980 1.85 0.24 1.32 ➾ 1981 1.91 0.24 1.27 alco.ix['Wyoming', 1999] ➾ Beer 1.41 ➾ Wine 0.18 ➾ Spirits 0.84 ➾ Name: (Wyoming, 1999), dtype: float64
pandas使用相同的方式处理多级索引和列,这使得索引级别可能会变成列级别,反之亦然。
堆叠和旋转
使用多级的列名可以将多级索引完全或部分扁平化,同样,使用多级索引则可以将多级列名完全或部分扁平化。
stack()函数会增加索引的级别数,同时减少列名的级别数。它使得frame更高更窄,如下图所示。如果列名已经扁平化了,则函数返回一个series。函数unstack()的作用相反:它减少索引的级别数,同时增加列名的级别数。它使得frame更短更宽。如果索引已经扁平化了,则函数返回一个series。
tall_alco = alco.stack() tall_alco.index.names += ["Drink"] tall_alco.head(10) ➾ State Year Drink ➾ Alabama 1977 Beer 0.99 ➾ Wine 0.13 ➾ Spirits 0.84 ➾ 1978 Beer 0.98 ➾ Wine 0.12 ➾ Spirits 0.88 ➾ 1979 Beer 0.98 ➾ Wine 0.12 ➾ Spirits 0.84 ➾ 1980 Beer 0.96 ➾ dtype: float64
上述操作的结果是一个具有三级索引的series(在这里我不得不提供缺少的第三级的名称—— “Drink”)。
wide_alco = alco.unstack() wide_alco.head() ➾ Beer ... ➾ Year 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 ... ➾ State ... ➾ Alabama 0.99 0.98 0.98 0.96 1.00 1.00 1.01 1.02 1.06 1.09 ... ➾ Alaska 1.19 1.39 1.50 1.55 1.71 1.75 1.76 1.73 1.68 1.68 ... ➾ Arizona 1.70 1.77 1.86 1.69 1.78 1.74 1.62 1.57 1.67 1.77 ... ➾ Arkansas 0.92 0.97 0.93 1.00 1.06 1.03 1.03 1.02 1.03 1.06 ... ➾ California 1.31 1.36 1.42 1.42 1.43 1.37 1.37 1.38 1.32 1.36 ... ➾ [5 rows x 99 columns]
上述操作的结果是具有扁平索引和两级分层列名的frame。你经常会在CSV和其他表格文件中看到这些类型的frame。为了使数据更加“方正”,更加易于管理,你可能需要将它们堆叠起来。
堆叠和拆分是更一般的绕轴旋转(pivoting)操作的特殊情况。函数pivot(index, columns,values)将frame转换为另一个frame,新的frame使用列索引作为新的索引,columns作为新的列名列表,values作为数据。
在下面的例子中,alco被重新组织成一个“方正”的frame,按年份(新的扁平索引)和州(列名)描述葡萄酒的消费情况:
alco.pivot("Year", "State", "Wine") ➾ State Alabama Alaska Arizona Arkansas California Colorado Connecticut ➾ Year ➾ 1977 0.13 0.42 0.34 0.10 0.67 0.36 0.35 ➾ 1978 0.12 0.45 0.37 0.11 0.68 0.47 0.38 ➾ 1979 0.12 0.47 0.39 0.10 0.70 0.47 0.40 ➾ 1980 0.16 0.50 0.36 0.12 0.71 0.47 0.43 ➾ «...» ➾ [33 rows x 51 columns]
如果index为None,则pandas会重新使用原始frame的索引。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论