[学习笔记]Python for Data Analysis, 3E-8.数据整理:连接、合并和重塑

在许多应用程序中,数据可能分布在多个文件或数据库中,或者以不便于分析的形式排列。本章重点介绍有助于合并、联接和重新排列数据的工具。
首先,介绍一下pandas中的分层索引的概念,这个概念在其中一些操作中被广泛使用。然后,我们深入研究特定的数据操作。你可以在’第十三章:数据分析示例’中看到这些工具的各种应用用法。

8.1分层索引

分层索引(Hierarchical indexing)是pandas的一项重要功能。它使你能够在一个轴上拥有多个(两个或多个)索引级别。另一个思考方式是,它为你提供了一种以较低维度的形式处理更高维度数据的方法。

# 创建一个Series,其中包含列表(或数组)作为它的索引
data = pd.Series(np.random.uniform(size=9), 
                 index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'], 
                        [1, 2, 3, 1, 3, 1, 2, 2, 3]])
# 返回一个多维索引作为索引的Series
data.index # 返回一个分层索引

# 对于分层索引对象,可以使用所谓的'部分索引',使你能够简洁地选择数据子集
data['b'] # 返回索引b对应的数据(带有子索引1和3)
data['b':'c'] # 返回索引b和c之间的数据(带有子索引)
data.loc[['b', 'd']] # 返回索引'b'和'd'对应的数据(带有子索引)
data.loc[:, 2] # 甚至可以从'内部'层进行选择,这里,我们选择所有第二级索引为2的值

# 分层索引在重塑数据和基于组的操作中(如形成数据透视表)中起着重要作用。例如,可以使用它的unstack方法将数据重新排列成一个DataFrame
data.unstack() # 通过unstack方法将分层索引的Series排列成DataFrame
# unstack的逆操作为stack
data.unstack().stack()
# stack和unstack在稍后的'重塑和旋转'部分会有更详细的探索

# 在DataFrame中,任一轴都可以有分层索引:
frame = pd.DataFrame(np.arange(12).reshape((4, 3)), 
                     index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], 
                     columns=[['Ohio', 'Ohio', 'Colorado'],
                              ['Green', 'Red', 'Green']]) # 这创建了一个index和columns都具有分层索引的DataFrame

# 分层水平可以具有名称(作为字符串或者任何Python对象),如果设置了名称,则它们的名称会显示在控制台输出中
frame.index.names=['key1', 'key2']
frame.columns.names = ['state', 'color']
# 在分层索引中,这些名称将取代name属性,而name属性仅仅用于单水平索引

注意:索引名称’state’和’color’不是行标签(frame.index值)的一部分,虽然在控制台输出中它们看上去像是。

# 你可以通过访问索引的nlevels属性来查看索引具有的级别数
frame.index.nlevels # 返回2

# 使用部分列索引,你也可以类似地选择列组
frame['Ohio'] # 返回列索引为'Ohio'对应的数据

# 一个多维索引也可以通过pd.MultiIndex.from_arrays方法创建,并在创建DataFame时使用
a = pd.MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'],
                              ['Green', 'Red', 'Green']],
                              names=['state', 'color'])
b = pd.DataFrame(np.arange(12).reshape((4, 3)), 
                 index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], 
                 columns=a)

重新排列和排序水平

有时,你可能需要在轴上重新排列水平的顺序或者在一个特定水平上根据值来排序数据。

# 重新排列水平的顺序:swaplevel方法以两个水平编号或者名称为输入,并返回一个级别互换的新对象(但数据在其他方面保持不变)
frame.swaplevel('key1', 'key2') # 将'key1'和'key2'两个水平互换,这等价于frame.swaplevel(0, 1)

# 特定水平上进行值排序:sort_index在默认情况下,使用所有索引级别按字典顺序对数据进行排序,但可通过传递level参数来选择优先使用的单个级别或级别子集进行排序
frame.sort_index(level=1) # 优先根据第1个水平(即key2)进行值排序,第1个水平值相同的值,再按照其他水平进行值排序
frame.swaplevel(0, 1).sort_index(level=0) # 先重新排列水平,再优先根据第0个水平进行排序

注意:如果分层索引对象是从最外层水平开始按照字典顺序排序索引的,即调用sort_index(level=0)或sort_index()之后的结果,则在上面进行数据选择的表现会更好。

按水平汇总统计数据

DataFrame和Series上的许多描述性和汇总统计数据都有一个水平选项,你可以在一个特定的轴上指定要聚合的水平。

# 考虑上面的DataFrame,我们可以在行或者列上按水平聚合
frame.groupby(level='key2').sum() # 按照行的'key2'水平分组,并按照sum()函数聚合
frame.groupby(level='color', axis='columns').sum() # 按照列的'color'水平分组,并按照sum()函数聚合
# 我们将在后面的'第10章:数组聚合和组操作'中更详细讨论groupby函数

使用DataFrame的列进行索引

想要使用DataFrame中的一列或多列作为行索引并不罕见;或者,你可能希望将行索引移动到DataFrame的列中。

frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),
                      'c': ['one', 'one', 'one', 'two', 'two', 'two', 'two'],
                      'd': [0, 1, 2, 0, 1, 2, 3]})

# DataFrame的set_index函数将使用一个或多个列作为索引来创建新的DataFrame
frame2 = frame.set_index(['c', 'd'])
# 默认情况下,这些列将从DataFrame中删除,但你可以通过传递drop=False到set_index函数中来保留它们:
frame.set_index(['c', 'd'], drop=False)

# reset_index则与set_index相反,它会将分层索引水平移到列上
frame2.reset_index()

8.2组合和合并数据集合

pandas对象中包含的数据可以通过多种方式进行组合:

  • pandas.merge
    基于一个或多个键连接DataFrame中的行。对于SQL或其他关系数据库的用户来说,这是熟悉的,因为它实现了数据库联接操作。
  • pandas.concat
    沿轴连接或’堆叠’对象。
  • combine_first
    将重叠的数据拼接在一起,以使用一个对象中的值填充另一个对象的缺失值。

我们将逐一讨论这些问题,并举例。它们将在本书其余部分的示例中使用。

数据库样式的DataFrame联接

合并或联接操作通过使用一个或多个键联接行来合并数据集。这些操作在关系数据库(如,基于SQL的数据库)中尤为重要。pandas中的pandas.merge函数是在数据上使用这些算法的主要入口点。

df1 = pd.DataFrame({"key": ["b", "b", "a", "c", "a", "a", "b"],
                    "data1": pd.Series(range(7), dtype="Int64")})
df2 = pd.DataFrame({"key": ["a", "b", "d"],
                    "data2": pd.Series(range(3), dtype="Int64")})
# 这是多对一联接的例子;df1中的数据有多行标签为'a'和'b',而在df2的'key'列中,每个值只有一行。
# 调用pandas.merge函数,则两个DataFrame会根据重叠的'key'列进行联接
pd.merge(df1, df2)

# 如果未指定要联接的列,则pandas.merge方法默认使用重叠的列名作为键。不过,最好还是传递on参数显式指定:
pd.merge(df1, df2, on='key')

# 通常,pandas.merge操作中列输出的顺序是未指定的
# 如果联接的对象中的列名不同,则可以通过left_on和right_on参数单独指定它们
df3 = pd.DataFrame({"lkey": ["b", "b", "a", "c", "a", "a", "b"],
                    "data1": pd.Series(range(7), dtype="Int64")})
df4 = pd.DataFrame({"rkey": ["a", "b", "d"],
                    "data2": pd.Series(range(3), dtype="Int64")})
pd.merge(df3, df4, left_on='lkey', right_on='rkey') # left_on中传递的参数一定是左边的DataFrame中的列名

# 你可能会注意到,结果中缺少'c'和'd'值和其关联的数据。默认情况下,pandas.merge做的是内联接,结果中的键是交集,活在两个表中找到的公共集。
# 其他可能的选项包括'left','right'和'outer'。外联接采用键的并集。
pd.merge(df1, df2, how='outer') # 对df1和df2使用外联接
pd.merge(df3, df4, left_on='lkey', right_on='rkey', how='outer')
# 在外联接中,不匹配的位置将出现NA值

下表是关于参数how的总结。

[表]how参数的不同联接类型

# 多对多合并形成匹配键的笛卡尔积
df1 = pd.DataFrame({"key": ["b", "b", "a", "c", "a", "b"],
                    "data1": pd.Series(range(6), dtype="Int64")})
df2 = pd.DataFrame({"key": ["a", "b", "a", "b", "d"],
                    "data2": pd.Series(range(5), dtype="Int64")})
pd.merge(df1, df2, on='key', how='left') # 将df1和df2按照'key'列做左联接
pd.merge(df1, df2, how='inner')

# 要按照多个键进行联接,则需要在on参数中传递列表
left = pd.DataFrame({"key1": ["foo", "foo", "bar"],
                     "key2": ["one", "two", "one"],
                     "lval": pd.Series([1, 2, 3], dtype='Int64')})
right = pd.DataFrame({"key1": ["foo", "foo", "bar", "bar"],
                      "key2": ["one", "one", "one", "two"],
                      "rval": pd.Series([4, 5, 6, 7], dtype='Int64')})
pd.merge(left, right, on=["key1", "key2"], how="outer")
# 在合并操作中要考虑的最后一个问题是如何处理重叠的列名
pd.merge(left, right, on='key1') # 默认会为来自左DataFrame的重名列名加上'_x'后缀,为来自右DataFrame的重名列名加上'_y'后缀
# 虽然可以通过重命名轴标签的方式解决重叠问题,但pandas.merge方法有一个suffixes选项用于指定字符串,这些字符串会被附加到左右DataFrame对象中的重叠名称上
pd.merge(left, right, on='key1', suffixes('_left', '_right'))

下表是pandas.merge上的参数参考。下一节将介绍如何使用DataFrame的行索引进行联接。

[表]pandas.merge函数参数

在行索引上合并

在某些情况下,DataFrame的合并键将在其索引(行标签)中找到。在这种情况下,你可以传递left_index=True或right_index=True(或者两者都)来指示索引被用于合并键

left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
                      'value': pd.Series(range(6), dtype='Int64')})
right1 = pd.DataFrame({'group_val': [3.5, 7]}, index = ['a', 'b'])
pd.merge(left1, right1, left_on='key', right_index=True) # 左DataFrame用'key'作联接键,右DataFrame用行索引作联接键

注意:仔细观察这里,你会看到left1的索引值被保留,而在上面的其他示例中,输入的DataFrame的行索引会被删除。这是因为right1的索引是唯一的,因此在这个’多对一’合并(使用默认的how='inner’方法)可以在输出中保留来自于left1的对应行的索引值。

# 默认的合并方法是联接键相交,你也可以更改为联接键的并集
pd.merge(left1, right1, left_on='key', right_index=True, how='outer')

对于分层索引数据,事情会更加复杂,因为索引上的联接等效于多键合并:

lefth = pd.DataFrame({"key1": ["Ohio", "Ohio", "Ohio", # 创建左DataFrame
                               "Nevada", "Nevada"],
                      "key2": [2000, 2001, 2002, 2001, 2002],
                      "data": pd.Series(range(5), dtype="Int64")})
righth_index = pd.MultiIndex.from_arrays([ # 创建右DataFrame的分层索引
        ["Nevada", "Nevada", "Ohio", "Ohio", "Ohio", "Ohio"],
        [2001, 2000, 2000, 2000, 2001, 2002]])
# 创建含分层行索引的右DataFrame
righth = pd.DataFrame({"event1": pd.Series([0, 2, 4, 6, 8, 10], dtype="Int64", index=righth_index),
                       "event2": pd.Series([1, 3, 5, 7, 9, 11], dtype="Int64", index=righth_index)})
# 根据两个Series获得DataFrame,必须将index设置在每个Series(也可以用数组设置值,然后统一设置index,最后利用astype将类型统一设置为'Int64')
pd.DataFrame({"event1": [0, 2, 4, 6, 8, 10],
              "event2": [1, 3, 5, 7, 9, 11]}, index=righth_index).astype('Int64')
# 在这种情况下,必须指示要合并为列表的多个列
pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)
pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True, how='outer')

# 左右DataFrame可以都使用行索引来进行合并
left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
                     index=["a", "c", "e"],
                     columns=["Ohio", "Nevada"]).astype("Int64")
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
                      index=["b", "c", "d", "e"],
                      columns=["Missouri", "Alabama"]).astype("Int64")
pd.merge(left2, right2, how='outer', left_index=True, right_index=True)

DataFrame有一个join的实例方法用来简化合并的过程。它还可用于组合许多具有相同或相似行索引但不重叠列的DataFrame对象,对于上面的例子,我可以改写为

left2.join(right2, how='outer') # left2和right2有类似索引以及不重叠的列,所以join方法会默认将两者的行索引作为联接键

# 与pandas.merge相比,DataFrame的join方法默认对联接键执行左联接。
# 它还支持在传递DataFrame的行索引与调用的DataFrame的一列联接
left1.join(right1, on='key') # left1的一列和right1的行索引进行联接
# 与pd.merge(left1, right1, left_on='key', right_index=True)不同的是,left1.join(right1, on='key')执行左联接,且原索引值不被保留
# 你可以将这个方法视为将数据联接到调用其方法的对象

最后,对于简单的多个DataFrame之间的索引对索引合并,你可以将DataFrame列表传递给join方法。下一节我们将介绍更通用的替代方法:pandas.concat函数。

another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
                       index=['a', 'c', 'e', 'f'],
                       columns=['New York', 'Oregon'])
left2.join([right2, another]) # left2左联接right2和another
left2.join([right2, another], how='outer') # left2外联接right2和another

沿轴串联

另一种数据组合操作可称为串联或堆叠。

# NumPy的concatenate函数可以串联或堆叠NumPy数组
arr = np.arange(12).reshape((3, 4))
np.concatenate([arr, arr], axis=1) # 跨列拼接数组

在pandas对象(如Series和DataFrame)的上下文中,具有标记的轴使你能够进一步推广数组串联。特别是,你还有许多其他顾虑:

  • 如果对象在其他轴上以不同的方式编制索引,我们是否应该组合这些轴中的不同元素,还是仅仅使用共同的值?
  • 串联的数据块是否需要在生成的对象中这样标识?
  • '串联轴’是否包含需要保留的数据?在许多情况下,DataFrame中的默认整数标签最好在串联过程中丢弃。

pandas中的concat函数提供了一种一致的方式来解决上述的每个问题。下面将举例说明它是如何工作的。

# 假设有三个没有索引重叠的Series
s1 = pd.Series([0, 1], index=['a', 'b'], dtype='Int64')
s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'], dtype='Int64')
s3 = pd.Series([5, 6], index=['f', 'g'], dtype='Int64')

# 调用pandas.concat函数可以将这些对象的值和索引粘合起来
pd.concat([s1, s2, s3]) # 将s1和s2和s3跨行串联
# 默认情况下,pandas.concat沿着axis='index'串联,会产生一个新的Series。
# 如果你传递axis='columns',则结果将会是一个DataFrame
pd.concat([s1, s2, s3], axis='columns')

# 在这种情况下,另一个轴上没有重叠,你能看到的是行索引的并集(外联接)
s4 = pd.concat([s1, s3])
pd.concat([s1, s4], axis='columns') # 跨列拼接,外联接
# 可以通过传递join='inner'使用行索引的交集
pd.concat([s1, s4], axis='columns', join='inner') # 跨列拼接,内联接

一个潜在的问题是,串联的片段在结果中不可识别。

# 假设你要在串联轴上创建一个分层索引,则可以通过传递key参数实现
result = pd.concat([s1, s1, s3], key=['one', 'two', 'three']) # 跨行拼接,并且为每个串联片段添加索引
result.unstack() # 将分层索引的Series展开成DataFrame

# 在沿着axis='columns'组合Series的情况下,keys会成为DataFrame列标题
pd.concat([s1, s2, s3], axis='columns', keys=['one', 'two', 'three'])

# 相同的逻辑也可以扩展到DataFrame对象
df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=["a", "b", "c"],
                   columns=["one", "two"])
df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=["a", "c"],
                   columns=["three", "four"])
pd.concat([df1, df2], axis="columns", keys=["level1", "level2"]) # 跨列拼接,keys参数用于创建分层索引,其中第一级可用于标识每个串联的DataFrame对象

# 如果在pd.concat函数中传递的是DataFrame对象的字典而不是DataFrame的列表,则字典键将用于keys参数
pd.concat({'level1': df1, 'level2': df2}, axis='columns')
# 通过names参数可以命名创建的轴级别
pd.concat([df1, df2], axis='columns', keys=['level1', 'level2'], names=['upper', 'lower']) # 'upper'为列的第一级的名称,'lower'为列的第二级的名称

# 最后一个注意事项涉及行索引不包含任何相关数据的DataFrame
df1 = pd.DataFrame(np.random.standard_normal((3, 4)), columns=['a', 'b', 'c', 'd'])
df2 = pd.DataFrame(np.random.standard_normal((2, 3)), columns=['b', 'd', 'a'])
# 这种情况下,可以传递ignore_index=True,它表明将丢弃每个DataFrame中的行索引,仅串联列中的值,并分配一个新的默认索引
pd.concat([df1, df2], ignore_index=True)

下表展示了控制如何创建分层索引的其他参数。

[表]pandas.concat函数参数

将重叠数据合并

还有另一种数据组合情况不能标识为merge或concatenation操作。你可能有两个数据集,其索引全部或部分重叠。作为一个启发的例子,考虑NumPy的where函数,它执行面向数组的if-else表达式的等效物:

a = pd.Series([np.nan, 2.5, 0.0, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a'])
b = pd.Series([0., np.nan, 2., np.nan, np.nan, 5.], index=['a', 'b', 'c', 'd', 'e', 'f'])
np.where(pd.isna(a), b, a) # 返回数组:如果pd.isna(a)元素为True,则选择对应下标的b的值,否则选对应的a的值
# 使用numpy.where不会检查索引标签是否对齐(但对象长度要相同,否则会报广播错误)
# 如果想要按索引排列值,可以用Series的combine_first方法(不要求对象长度相同)
a.combine_first(b) # 返回Series:索引为两个对象索引的并集,按照字典顺序排列,对某个索引,如'a',先查看Series a中是否为null或不存在,如果非null,则选a的值,否则选b的值

# DataFrame的combine_first方法逐列执行相同的操作,
df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan],
                    'b': [np.nan, 2., np.nan, 6.],
                    'c': range(2, 18, 4)})
df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.],
                    'b': [np.nan, 3., 4., 6., 8.]})
df1.combine_first(df2) # 返回DataFrame:索引和列标签为两个对象的索引和列标签的并集,并按照df1优先的原则选择
# DataFrame对象的combine_first方法的结果会有列名的并集

8.3重塑和旋转

有许多用于重新排列表格数据的基本操作。这些操作称为reshape或pivot操作。

使用分层索引进行重塑

分层索引提供了一种在DataFrame中重新排列数据的一致方法。有两个主要操作:

  • stack
    这会’旋转’数据中的列到行。
  • unstack
    这将从行转入列
# 考虑一个小型DataFrame,其中字符串数组作为行和列索引
data = pd.DataFrame(np.arange(6).reshape((2, 3)), # 默认reshape按行优先排列(等效于reshape((-1, 3), order='c'),要按列优先,则需要order='f'
                    index=pd.Index(['Ohio', 'Colorado'], name='state'),
                    columns=pd.Index(['one', 'two', 'three'], name='number'))
# 对数据使用stack方法将列旋转到行中,从而生成一个有分层索引的Series
result = data.stack()

# 对一个有分层索引的Series,可以使用unstack方法将数据重新排列回DataFrame
result.unstack()

# 默认情况下,最内层先被unstack展开(堆叠stack的时候也是从最内层开始)
# 但也可以通过传递层编号或名称来展开别的层
result.unstack(level=0) # unstack第0层的索引
result.unstack(level='state') # unstack 'state'层

# 对于每个子组中都找不到的层中的值,unstack会引入缺失值
s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'], dtype='Int64')
s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'], dtype='Int64')
data2 = pd.concat([s1, s2], keys=['one', 'two']) # 将s1和s2拼接成分层索引的Series
data2.unstack()
# 默认情况下,stack会过滤掉缺失值,这导致先unstack再stack可以将数据还原
data2.unstack().stack()
data2.unstack().stack(dropna=False) # 不过滤掉缺失值

# 在DtaFrame上unstack时,被展开的层会成为结果列的最低层
df = pd.DataFrame({'left': result, 'right: result + 5},
                  columns=pd.Index(['left', 'right'], name='side'))
df.unstack(level='state')

# 与unstack一样,在调用stack函数时,我们可以指示要堆叠的轴的名称
df.unstack(level='state').stack(level='side')

将’长’旋转为’宽’格式

在数据库和CSV文件中存储多时间序列的常用方法是有时称为长(long)或堆叠(stacked)的格式。在这个格式中,单个值由表中的单个行表示,而不是由每行中的多个值表示。

# 加载一些示例数据,并进行少量的时间序列整理和其他数据清理
data = pd.read_csv('examples/macrodata.csv')
data = data.loc[:, ['year', 'quarter', 'realgdp', 'infl', 'unemp']]
data.head()
# 首先,使用将在'第11章:时间序列'中详细讨论的pandas.PeriodIndex函数(表示时间间隔而不是时间点)来组合year和quarter列,以将索引设置为每个季度第一天的datetime
periods = pd.PeriodIndex(year=data.pop('year'), # pop方法可以返回DataFrame中的一个Series列,且原DataFrame中不保留该Series列
                         quarter=data.pop('quarter'),
                         name='date') # 利用pandas.PeriodIndex组合year和quarter,返回元素为period[Q-DEC]类型的pd.PeriodIndex
data.index = periods.to_timestamp('D') # 利用pd.PeriodIndex的to_timestamp方法获得每个季度的第一天的datetime,并赋值作为data的索引
# 然后,利用reindex方法选择列的子集,并将列索引命名为'item'
data = data.reindex(columns=['realgdp', 'infl', 'umemp'])
data.columns.name = 'item'
# 最后,用stack函数进行重塑,用reset_index()函数将新的索引层转化为列,最后将包含数据值的列重命名为'value'
long_data = (data.stack() # 将DataFrame堆叠为分层索引的Series
             .reset_index() # 将分层索引转化为列(则多了两个新列),新索引为默认自然数索引,返回DataFrame,此时原Series值列的列名为0
             .rename(columns={0: 'value'})) # 将原Series值列的列名重命名为'value'

在这种用于多时间序列的所谓的长格式中,表中的每一行都表示一个观测值。
作为一种随着数据增加,允许item列中非重复值的数量改变的固定架构(列名和数据类型),数据通常以这种方式存储在关系SQL数据库中。在前面的示例中,date和item通常是主键(关系数据库术语),提供关系完整性和更简单的联接。在某些情况下,以这种格式处理数据可能更困难;你也许希望有一个DataFrame,每个不同的item值被date列的时间戳索引。DataFrame的pivot方法完全执行了这种转换:

pivoted = long_data.pivot(index='date', columns='item', values='value') # 索引为'date',同一个时间戳对应的不同的item值在不同的列上
# 传递的前两个参数要分别用作行索引和列索引,最后是用于填充DtaFrame的可选值列

# 假设你有两个要同时重塑的值列
long_data['value2'] = np.random.standard_normal(len(long_data)) # 在longdata上添加一个新的值列'value2'
# 通过在pivot方法中省略最后一个参数,你可以获得一个包含分层列的DataFrame
pivoted = long_data.pivot(index='data', 'columns'='item')

# 注意,pivot方法等效于用set_index方法创建一个分层索引后再调用unstack方法
unstacked = long_data.set_index(['date', 'item']).unstack(level='item') # 先将['date', 'item']作为分层索引,在将'item'层展开

将’宽’旋转为’长’格式

与DataFrame中的pivot函数相反的操作是pandas.melt函数。它不是将一列转换为多列的新DataFrame,而是将多列合并为一列,从而生成比输入更长的DataFrame。

df = pd.DataFrame({'key': ['foo', 'bar', 'baz'],
                   'A': [1, 2, 3],
                   'B': [4, 5, 6],
                   'C': [7, 9, 9]})

# 'key'列可以是组指标,其他列是数据值。
# 使用pandas.melt函数时,索引必须是默认索引,通过id_vars参数指示哪些列(如果有的话)是组指标。这里我们用'key'列作为唯一的组指标
melted = pd.melt(df, id_vars='key') # 原列名会作为值出现在'variable'列下,值会出现在'value'列下
# 使用pivot函数和reset_index函数可以将形状改回原始布局
reshaped = melted.pivot(index='key', columns='variable', values='value')
reshape.reset_index() # 由于pivot函数将index参数作为行标签,因此需要用reset_index()函数重置索引

# 还可以通过value_vars参数指定被用于value列的的列子集
pd.melt(df, id_vars='key', value_vars=['A', 'B'])

# pandas.melt函数也可以在没有组指标的情况下使用,此时只有值变量value_vars和值value
pd.melt(df, value_vars=['A', 'B', 'C']) # 此时'key'列的信息就丢失了
pd.melt(df, vlaue_vars=['key', 'A', 'B'])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值