print(f'行索引级别数: {stocks_reshaped.index.nlevels}')
print(f'列索引级别数: {stocks_reshaped.columns.nlevels}')6 数据整理:连接、合并与重塑
在大多数真实世界的应用中,你所需要的数据往往分散在多个文件、数据库或API中。这些数据很少会以一种立即可用于分析的格式呈现。本章将重点介绍 pandas 中用于合并、连接和重排数据,从而将其整理成干净、便于分析的格式的核心工具。
首先,我们将介绍分层索引 (Hierarchical Indexing) 的概念,这是 pandas 的一个强大功能,它允许我们构建更复杂的数据结构。这将是后续许多操作的基石。然后,我们将深入探讨数据操作的具体机制,并通过源自中国经济与金融的实际案例,将这些抽象的工具付诸实践。
6.1 分层索引
分层索引是 pandas 的一个关键特性,它允许你在单个轴上拥有多个(两个或更多)索引层级。你可以将其视为一种在熟悉的二维 DataFrame 结构中表示更高维度数据的方法。这在经济学中对于处理面板数据(Panel Data)尤其有用,面板数据旨在追踪多个主体(如不同省份或公司)在一段时间内的变化。
让我们从一个实际的例子开始。我们将从美国联邦储备经济数据库(FRED)获取中国几个核心经济指标的季度数据:实际国内生产总值(RGDP)、居民消费价格指数(CPI)和工业生产总值(IPI)。这类数据天然适合使用两级索引:一级是指标名称,另一级是日期。
将多重索引想象成电子表格中的“合并单元格”功能。在外层索引(例如,经济指标)下,包含了所有内层索引(例如,每个季度的日期)。这种结构让你能够用二维的表格清晰地表示三维甚至更高维度的数据(例如,一个国家的多项指标在多个时间点上的值)。在计量经济学中,这对于处理面板数据(Panel Data)至关重要。
首先,让我们创建一个带有这种多级索引的 Series。
import pandas as pd
import numpy as np
from fredapi import Fred
# 初始化 FRED API 连接
fred_key = 'f2a2c60b6dc82682031f4ce84bf6da18'
fred = Fred(api_key=fred_key)
# 定义中国关键经济指标的FRED序列ID
series_ids = {
'RGDP': 'NGDPRSAXDCCHQ', # 实际GDP
'CPI': 'CHNCPIALLQINMEI', # 居民消费价格指数
'IPI': 'CHNPROINDQISMEI' # 工业生产指数
}
# 获取并合并数据
all_series = []
for indicator, series_id in series_ids.items():
s = fred.get_series(series_id, start_date='2010-01-01', end_date='2023-12-31')
s = s.dropna()
s.name = 'Value'
# 通过添加指标名称创建多重索引
s.index = pd.MultiIndex.from_product([[indicator], s.index], names=['indicator', 'date'])
all_series.append(s)
# 将所有 Series 连接成一个
data = pd.concat(all_series)
# 显示结果序列的头部
print(data.head())你在 列表 lst-hierarchical-series 的输出中看到的是一个 Series 的“美化”视图,它的索引是一个 MultiIndex。外层 ‘indicator’ 级别中的空白意味着“使用正上方的标签”。让我们来检查一下索引对象本身。
data.index对于一个分层索引的对象,所谓的部分索引 (partial indexing) 是可行的,它使你能够简洁地选择数据的子集。例如,我们可以轻松地选择所有的CPI数据。
data['CPI']你也可以像对标准索引一样,对外层索引进行切片操作。
data['CPI':'IPI']甚至可以从“内层”级别进行选择。在这里,我们可以选择所有指标在特定日期的数据。为此,我们使用 .loc 索引器。第一个位置的冒号 : 表示我们想要选择第一个(外层)索引级别中的所有项。
loc 索引器的使用技巧
在处理多重索引时,.loc 是你最强大的工具之一。它的语法 .loc[(level_0_selection, level_1_selection, ...)] 允许你对任意层级的索引进行精确选择和切片。使用冒号 : 作为占位符,可以表示“选择该层级的所有标签”。例如,data.loc[:, '2022-01-01'] 的意思是“在第0层(指标)选择所有,在第1层(日期)选择‘2022-01-01’”。
# 选择 2022 年第一季度的数据
data.loc[:, '2022-01-01']6.1.1 使用 stack 和 unstack 进行重塑
分层索引在数据重塑中扮演着重要角色。一个常见的操作是将带有 MultiIndex 的 Series 重排成 DataFrame。这可以通过 unstack() 方法完成,该方法会将最内层的索引级别旋转到列索引中。
unstack() 将中国经济指标数据重塑为 DataFrame
data_unstacked = data.unstack()
data_unstacked.head()unstack() 的逆操作是 stack(),它将列旋转到行中,再次生成一个 Series。这些操作对于在“长”格式和“宽”格式数据之间转换至关重要,我们将在 sec-reshaping-pivoting 中深入探讨这个主题。
# 注意:原始数据中存在缺失值,stack 默认会移除它们
# 这就是为什么如果某些指标缺少某些季度的数据,输出会变短
data_unstacked.stack().head()6.1.2 DataFrame 上的分层索引
对于 DataFrame,行轴和列轴都可以拥有分层索引。让我们构建一个更复杂的例子。我们将使用 yfinance 库获取两家中国顶尖科技公司——腾讯(0700.HK)和阿里巴巴(9988.HK)的开盘价、收盘价和日交易量。这将允许我们创建一个在行(股票代码、日期)和列(度量类型)上都具有 MultiIndex 的 DataFrame。
import yfinance as yf
# 下载股票数据
tencent = yf.download('0700.HK', start='2023-01-01', end='2023-01-10', progress=False)
alibaba = yf.download('9988.HK', start='2023-01-01', end='2023-01-10', progress=False)
# 为每个 DataFrame 添加一个 'ticker' 列
tencent['ticker'] = 'Tencent'
alibaba['ticker'] = 'Alibaba'
# 连接并设置 MultiIndex
stocks = pd.concat([tencent, alibaba])
stocks = stocks.reset_index().set_index(['ticker', 'Date'])
# 为列创建 MultiIndex
stocks_reshaped = stocks[['Open', 'Close', 'Volume']].copy()
stocks_reshaped.columns = pd.MultiIndex.from_tuples([
('Price', 'Open'),
('Price', 'Close'),
('Volume', 'Total')
])
stocks_reshaped分层索引的每个级别都可以有名称。让我们来为它们命名。
stocks_reshaped.index.names = ['Ticker', 'TradeDate']
stocks_reshaped.columns.names = ['Category', 'Metric']
stocks_reshaped我们可以通过访问索引的 nlevels 属性来查看它有多少个级别:
通过部分列索引,你同样可以选择列的分组。例如,要选择所有的 ‘Price’ 数据:
Price 类别下的所有列
stocks_reshaped['Price']6.1.3 重排序和排序级别
有时,你可能需要重新排列轴上级别的顺序,或者根据某个特定级别中的值对数据进行排序。swaplevel() 方法接收两个级别编号或名称,并返回一个级别互换的新对象(但数据本身保持不变)。
Ticker 和 TradeDate 索引级别
stocks_reshaped.swaplevel('Ticker', 'TradeDate')sort_index() 默认使用所有索引级别按字典顺序对数据进行排序。你可以选择按特定级别排序。例如,让我们按 TradeDate 排序。
TradeDate 级别对 DataFrame 进行排序
stocks_reshaped.sort_index(level='TradeDate')词典排序就像按字母顺序排列单词一样。对于多重索引,pandas 会首先按最外层索引排序,然后在每个外层索引的组内,再按次外层索引排序,以此类推。在分层索引的对象上,如果索引是按词典顺序从最外层开始排序的,那么数据选择的性能会好得多。调用 sort_index(level=0) 或 sort_index() 就能实现这一点。
6.1.4 按级别进行汇总统计
DataFrame 和 Series 上的许多描述性和汇总统计都有一个 level 选项,你可以用它来指定在特定轴上进行聚合的级别。思考一下我们在 表 tbl-hierarchical-df 中创建的股票 DataFrame。我们可以在行或列上按级别进行聚合。例如,让我们计算每个股票代码的所有指标的平均值。
Ticker 的平均统计数据
stocks_reshaped.groupby(level='Ticker').mean()现在,让我们在列轴上计算每天每个类别的总和。
Category 的总和
# 这个操作只需要数值型数据
stocks_reshaped.groupby(level='Category', axis='columns').sum().head()6.1.5 使用 DataFrame 的列进行索引
从文件中加载数据时,期望的索引通常存储在一个或多个常规列中。set_index 函数可以使用一个或多个列作为索引来创建一个新的 DataFrame。让我们使用世界银行的数据为例,这些数据通常以“扁平”格式提供。我们将关注中国及其主要的亚洲贸易伙伴。
import wbdata
# 定义指标和国家
indicators = {'NY.GDP.MKTP.CD': 'GDP', 'SP.POP.TOTL': 'Population'}
# 关注中国及周边主要经济体
countries = ['CHN', 'JPN', 'KOR', 'SGP']
# 获取数据
df_flat = wbdata.get_dataframe(indicators, country=countries, convert_date=True)
df_flat = df_flat.dropna().reset_index()
df_flat.head()现在,我们使用 set_index 从 ‘country’ 和 ‘date’ 列创建一个 MultiIndex。
df_indexed = df_flat.set_index(['country', 'date'])
df_indexed.head()默认情况下,用作索引的列会从 DataFrame 中移除。你可以通过传递 drop=False 来保留它们。
set_index 同时保留原始列
df_flat.set_index(['country', 'date'], drop=False).head()另一方面,reset_index 的作用与 set_index 相反;分层索引的级别会被移回到列中。
reset_index() 将分层索引级别移回列中
df_indexed.reset_index().head()6.2 合并与连接数据集
pandas 对象中的数据可以通过几种方式进行组合:
pandas.merge: 基于一个或多个键连接DataFrame中的行。这是数据库join操作的基石。pandas.concat: 沿着一个轴连接或“堆叠”对象。combine_first: 将重叠的数据拼接在一起,用一个对象中的值填充另一个对象中的缺失值。
我们将通过应用实例来逐一介绍这些方法。
6.2.1 数据库风格的 DataFrame 连接
合并 (Merge) 或 连接 (Join) 操作通过使用一个或多个键来链接行,从而组合数据集。这些是关系型数据库(例如 SQL)中的基本操作。pandas.merge 函数是在你的数据上使用这些算法的主要入口点。
让我们构建一个场景。我们希望分析中国经济增长与利率水平之间的关系。我们将获取中国的季度实际GDP(NGDPRSAXDCCHQ)和作为基准利率的贷款市场报价利率(LPR, DBLPR1Y)。由于LPR是月度数据,我们需要将其重采样为季度数据。
# 获取中国季度实际GDP
gdp = fred.get_series('NGDPRSAXDCCHQ', start_date='2015-01-01', end_date='2023-12-31').dropna()
gdp = gdp.to_frame(name='gdp')
gdp['year'] = gdp.index.year
# 获取中国贷款市场报价利率 (LPR, 月度)
lpr = fred.get_series('DBLPR1Y', start_date='2015-01-01', end_date='2023-12-31').dropna()
# 通过取平均值重采样为季度数据
lpr_q = lpr.resample('QS').mean().to_frame(name='lpr_rate')
lpr_q['year'] = lpr_q.index.year
print('--- GDP 数据 (df1) ---')
print(gdp.head())
print('\n--- LPR 利率数据 (df2) ---')
print(lpr_q.head())这是一个一对一 (one-to-one) 连接的例子,因为在按季度重采样后,两个DataFrame的日期索引都是唯一的。我们可以直接在日期索引上进行合并。
# 在这里,基于索引的合并更清晰。我们将 right_index 和 left_index 设置为 True
merged_data = pd.merge(gdp, lpr_q, left_index=True, right_index=True)
merged_data.head()请注意,当在索引上合并时,我不需要指定要连接的列。如果是在列上连接,pandas.merge 会使用重叠的列名作为键。一个好的做法是使用 on 参数明确指定。让我们尝试在 year 列上合并。
year 列上显式合并
# 注意:这将为每年创建一个笛卡尔积
pd.merge(gdp, lpr_q, on='year').head(8)默认情况下,pandas.merge 执行的是内连接 (inner join)。结果中的键是两个表中键的交集,即共同的部分。其他选项是 "left"、"right" 和 "outer"。outer 连接取键的并集。让我们创建两个略有不同的数据集来说明这一点。
我们将获取中国的城镇登记失业率(LMUNRRTTCNM156S)和CPI(CHNCPIALLMINMEI),但选取的时间范围略有不同。
unemp = fred.get_series('LMUNRRTTCNM156S', start_date='2018-01-01', end_date='2021-12-31')
unemp = unemp.resample('QS').mean().to_frame(name='unemployment')
infl = fred.get_series('CHNCPIALLMINMEI', start_date='2020-01-01', end_date='2023-12-31')
infl = infl.resample('QS').mean().to_frame(name='inflation')
print('--- 失业率数据 (df1) ---')
print(unemp)
print('\n--- 通货膨胀率数据 (df2) ---')
print(infl)现在,让我们执行一个 outer 连接。
pd.merge(unemp, infl, left_index=True, right_index=True, how='outer')在 outer 连接中,左侧(unemp)或右侧(infl)DataFrame 对象中,如果某行的键在另一个 DataFrame 中不匹配,那么在另一个 DataFrame 的列中将显示为 NaN 值。表 tbl-join-types 总结了 how 选项。
| 选项 | 行为 |
|---|---|
how='inner' |
只使用在两个表中都观察到的键组合(交集)。 |
how='left' |
使用在左表中找到的所有键组合。 |
how='right' |
使用在右表中找到的所有键组合。 |
how='outer' |
使用在两个表中观察到的所有键组合(并集)。 |
多对多 (Many-to-many) 合并会形成匹配键的笛卡尔积。这是一个至关重要的概念。想象一下,你有一个按季度报告的公司财务数据集,和另一个这些公司的分析师评级数据集,其中多个分析师可能在任何给定的季度发布评级。
在多对多合并中,左表中每个键的每一行都会与右表中同一个键的每一行进行匹配。如果左表有 \(m\) 行对应键 k,右表有 \(n\) 行对应键 k,那么合并后的结果将包含 \(m \times n\) 行对应键 k。这可能导致数据量急剧膨胀,并且在不经意间产生重复或无意义的数据。在使用合并操作前,务必检查你的键是否唯一,并清楚地了解你所期望的连接关系(一对一,一对多,还是多对多)。
让我们用腾讯和阿里的例子模拟这种情况。
financials = pd.DataFrame({
'ticker': ['Tencent', 'Tencent', 'Alibaba', 'Alibaba'],
'quarter': ['2023Q1', '2023Q2', '2023Q1', '2023Q2'],
'revenue': [1499.9, 1492.1, 2377.6, 2329.3]
})
ratings = pd.DataFrame({
'ticker': ['Tencent', 'Tencent', 'Tencent', 'Alibaba'],
'quarter': ['2023Q1', '2023Q1', '2023Q2', '2023Q1'],
'analyst': ['中信证券', '高盛', '摩根大通', '中信证券'],
'rating': ['买入', '买入', '持有', '买入']
})
pd.merge(financials, ratings, on=['ticker', 'quarter'])注意,对于腾讯在 2023Q1,单一的收入数据与两个分析师评级都匹配了,结果产生了相应的两行数据。
连接时,你可能会有不属于连接键的重叠列名。pandas.merge 有一个 suffixes 选项,可以为这些列名附加字符串。
df_left = pd.DataFrame({'key': ['a', 'b'], 'data': [1, 2]})
df_right = pd.DataFrame({'key': ['a', 'b'], 'data': [3, 4]})
pd.merge(df_left, df_right, on='key', suffixes=('_left_data', '_right_data'))pd.merge 的完整参数列表在 表 tbl-merge-args 中提供。
| 参数 | 描述 |
|---|---|
left |
位于左侧要合并的 DataFrame。 |
right |
位于右侧要合并的 DataFrame。 |
how |
应用的连接类型:'inner'、'outer'、'left' 或 'right' 之一;默认为 'inner'。 |
on |
用于连接的列名。必须在两个 DataFrame 对象中都存在。 |
left_on |
左侧 DataFrame 中用作连接键的列。 |
right_on |
右侧 DataFrame 中用作连接键的列。 |
left_index |
使用左侧的行索引作为其连接键。 |
right_index |
使用右侧的行索引作为其连接键。 |
sort |
按连接键对合并后的数据进行词典排序;默认为 False。 |
suffixes |
附加到重叠列名上的字符串元组。 |
validate |
验证合并是否为指定类型(例如,'one_to_one')。 |
indicator |
添加一个特殊的 _merge 列,指示每行的来源。 |
6.2.2 按索引合并
正如我们在 表 tbl-merge-many-to-one 中看到的,在某些情况下,DataFrame 中的合并键可能位于其索引中。在这种情况下,你可以传递 left_index=True 或 right_index=True(或两者都传)来指示应使用索引作为合并键。这在处理时间序列数据时极为常见。让我们重新审视我们的 unemp 和 infl DataFrame。
pd.merge(unemp, infl, left_index=True, right_index=True, how='inner')对于分层索引的数据,按索引连接等同于多键合并。
DataFrame.join 方法提供了一种便捷的按索引合并的方式。它默认执行左连接。
merge vs. join 的区别
pandas 提供了 merge() 函数和 DataFrame.join() 方法,两者功能相似但用法不同,初学者容易混淆。 - merge() 是一个通用的函数,默认通过列名进行连接,但可以通过 left_index=True 和 right_index=True 指定使用索引。它更加灵活,支持所有类型的连接 (inner, left, right, outer)。 - join() 是 DataFrame 的一个实例方法 (df1.join(df2)),它默认通过索引进行连接。它默认执行左连接 (how='left')。虽然它也支持通过 on 参数指定 df1 中的列与 df2 的索引进行连接,但其主要设计目的是为了方便地按索引合并。
经验法则:当你需要基于列进行复杂的数据库式连接时,优先使用 pd.merge()。当你需要快速地将两个或多个 DataFrame 按索引合并时,df.join() 更为便捷。
让我们使用 .join() 来重现之前的合并操作。
.join() 方法进行基于索引的合并
# .join() 默认执行左连接
unemp.join(infl)为了得到与之前相同的 inner 连接结果,我们指定 how='inner'。
.join() 方法执行内连接
unemp.join(infl, how='inner')6.2.3 沿轴连接
另一种数据组合方式被称为连接 (concatenation) 或堆叠 (stacking)。pandas.concat 是用于此操作的主要函数。让我们创建一个场景,我们有两个独立的中国城镇失业率数据集:一个用于2018-2019年,另一个用于2020-2021年。我们想将它们组合成一个单一的时间序列。
unemp_s1 = fred.get_series('LMUNRRTTCNM156S', start_date='2018-01-01', end_date='2019-12-31')
unemp_s2 = fred.get_series('LMUNRRTTCNM156S', start_date='2020-01-01', end_date='2021-12-31')
# 连接两个 series
unemp_full = pd.concat([unemp_s1, unemp_s2])
print(unemp_full.head())
print('...')
print(unemp_full.tail())默认情况下,pd.concat 沿 axis=0(行索引)工作。如果你传递 axis=1(axis='columns'),结果将是一个 DataFrame。
pd.concat([unemp_s1, unemp_s2], axis=1).head()一个潜在的问题是,在结果中无法识别连接的各个部分。假设你希望在连接轴上创建一个分层索引。为此,可以使用 keys 参数。
result = pd.concat([unemp_s1, unemp_s2], keys=['2018-2019', '2020-2021'])
result同样的逻辑也适用于 DataFrame 对象。让我们创建两个包含不同中国经济指标的 DataFrame 并将它们连接起来。
df1 = fred.get_series('NGDPRSAXDCCHQ', start_date='2020-01-01', end_date='2020-12-31').to_frame(name='gdp')
df2 = fred.get_series('LMUNRRTTCNM156S', start_date='2020-01-01', end_date='2020-12-31').to_frame(name='unemployment')
pd.concat([df1, df2], axis=1)最后一个需要考虑的问题是,DataFrame 的行索引不包含任何相关数据的情况。在这种情况下,你可以传递 ignore_index=True,这将丢弃原始索引并分配一个新的默认整数索引。
df1.reset_index(drop=True, inplace=True)
df2.reset_index(drop=True, inplace=True)
pd.concat([df1, df2], ignore_index=True)6.2.4 合并重叠数据
还有一种数据组合情况,既不能表示为合并也不能表示为连接操作。你可能有索引完全或部分重叠的两个数据集,并且你希望用一个数据集中的值来“修补”另一个数据集中的缺失值。这在处理来自不同来源且可能存在数据缺口的数据时是一项常见任务。combine_first 方法就是为此设计的。
让我们创建两个 Series,它们有一些重叠的索引和 NaN 值。
s1 = pd.Series([np.nan, 10, np.nan, 30], index=['a', 'b', 'c', 'd'], name='s1')
s2 = pd.Series([100, np.nan, 300, 400], index=['a', 'b', 'c', 'd'], name='s2')
# 当 s1 中有 NaN 时,使用 s2 中的值
s1.combine_first(s2)对于 DataFrame,combine_first 对每一列执行相同的操作。因此,你可以将其视为用你传递的对象中的数据“修补”调用对象中的缺失数据。
df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan],
'b': [np.nan, 2., np.nan, 6.]})
df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.],
'b': [np.nan, 3., 4., 6., 8.]})
df1.combine_first(df2)使用 DataFrame 对象时,combine_first 的输出将包含所有列名的并集。
6.3 重塑与透视
有许多用于重排表格数据的基本操作。这些操作被称为重塑 (reshape) 或透视 (pivot) 操作。
6.3.1 使用分层索引进行重塑
正如我们在 sec-stack-unstack 中看到的,分层索引为在 DataFrame 中重排数据提供了一种一致的方式。两个主要操作是: * stack: 这个操作将数据从列“旋转”或透视到行。 * unstack: 这个操作将数据从行透视到列。
让我们用一个清晰的例子再次说明这些操作。我们将构建一个包含中国GDP增长率和工业生产增长率的 DataFrame。
cn_gdp = fred.get_series('NGDPRSAXDCCHQ', start_date='2021-01-01', end_date='2021-12-31').pct_change().dropna()
cn_ipi = fred.get_series('CHNPROINDQISMEI', start_date='2021-01-01', end_date='2021-12-31').pct_change().dropna()
data = pd.DataFrame({'GDP_Growth': cn_gdp, 'IPI_Growth': cn_ipi})
data.index.name = 'Quarter'
data.columns.name = 'Indicator'
data对这个数据使用 stack 方法会将列透视到行,生成一个带有 MultiIndex 的 Series。
result = data.stack()
result从这个分层索引的 Series 中,你可以使用 unstack 将数据重新排列回 DataFrame。
Indicator 索引级别透视回列
result.unstack()默认情况下,最内层的级别被 unstack。你可以通过传递级别编号或名称来 unstack 不同的级别。例如,让我们 unstack ‘Quarter’ 级别。
Quarter 级别名称进行 unstack
result.unstack(level='Quarter')6.3.2 将“长”格式透视为“宽”格式
在数据库和CSV文件中存储多个时间序列的一种常见方式是所谓的长格式 (long) 或堆叠格式 (stacked)。在这种格式中,每一行都是一个单独的观测值。这通常是用于存储和某些类型分析的首选“整洁”数据格式。
让我们使用 FRED 中中国 realgdp、infl 和 unemp 的数据创建一个长格式数据集。
# 获取数据
gdp = fred.get_series('NGDPRSAXDCCHQ', '2020-01-01', '2021-12-31')
infl = fred.get_series('CHNCPIALLMINMEI', '2020-01-01', '2021-12-31').resample('QS').first()
unemp = fred.get_series('LMUNRRTTCNM156S', '2020-01-01', '2021-12-31').resample('QS').first()
# 首先合并成一个宽格式 DataFrame
wide_data = pd.DataFrame({'realgdp': gdp, 'infl': infl, 'unemp': unemp})
wide_data.index.name = 'date'
wide_data.columns.name = 'item'
# 堆叠以创建长格式数据
long_data = (wide_data.stack()
.reset_index()
.rename(columns={0: 'value'}))
long_data.head(10)在这种长格式中,每一行代表一个单一的观测值(特定日期的特定项目)。这是关系型数据库中常见的结构。然而,对于时间序列分析或某些类型的建模,你可能更喜欢宽格式 (wide),即每个不同的 item 都有一列。pivot 方法正是执行这种转换。
pivot 将数据从长格式转换为宽格式
pivoted = long_data.pivot(index='date', columns='item', values='value')
pivoted.head()pivot 的前两个参数分别是将用作行索引和列索引的列。values 参数是包含要填充 DataFrame 的数据的列。如果省略 values,并且你有多个剩余的列,你将得到一个带有分层列的 DataFrame。
6.3.3 将“宽”格式透视为“长”格式
对于 DataFrame,pivot 的逆操作是 pandas.melt。它不是将一列转换为多列,而是将多列合并为一列,生成一个比输入更长的 DataFrame。
让我们从一个简单的宽格式 DataFrame 开始,以中国知名公司为例。
df = pd.DataFrame({'公司名称': ['华为', '腾讯', '阿里巴巴'],
'收入_2022': [6423, 5545, 8645],
'收入_2023': [7042, 5601, 8791]})
df'公司名称' 列可以是一个分组指标,其他列是数据值。使用 pandas.melt 时,我们必须指明哪些列是分组指标(id_vars)。
melt 将宽格式数据转换为长格式
melted = pd.melt(df, id_vars='公司名称')
melted你可以使用 pivot 将其重塑回原始布局。
reshaped = melted.pivot(index='公司名称', columns='variable', values='value')
reshaped.reset_index()你还可以指定一个列的子集作为值列(value_vars)。
melt 并指定 value_vars
pd.melt(df, id_vars='公司名称', value_vars=['收入_2022'])6.4 结论
同学们,现在你们已经对 pandas 在数据导入、清洗和重组方面的核心工具有了基础的理解。这些技能——分层索引、合并、连接和重塑——不仅仅是机械的步骤。它们是连接原始、混乱的现实世界信息与严谨的计量经济和金融分析所需结构化数据之间的重要桥梁。有效整理数据的能力,将最终决定你们实证结论的质量和可靠性。