引言:数据整理 - 连接、合并和重塑 🗂️

  • 在许多应用中,数据分散在多个文件或数据库中。
  • 数据的组织形式也可能不利于分析。
  • 本章重点介绍用于有效组合、连接和重新排列数据的工具。
  • 关键概念:pandas 中的分层索引 (Hierarchical Indexing)。

引言:数据整理可视化

数据整理、数据挖掘和机器学习

什么是数据整理?

  • 数据整理(或数据重整)是将数据从一种“原始”数据形式转换和映射为另一种格式的过程。
  • 目的是使其更适合和有价值地用于各种下游目的,例如分析。

数据挖掘和机器学习

什么是数据挖掘?

  • 数据挖掘 是在大型数据集中发现模式、异常和相关性以预测结果的过程。

什么是机器学习?

  • 机器学习 是人工智能的一个子领域。
  • 它专注于开发可以从数据中学习并根据数据做出决策/预测的系统。
  • 监督学习:使用标记数据集来训练算法。
  • 无监督学习:在未标记数据中发现隐藏模式。
  • 强化学习:智能体通过与环境交互来学习。

Pandas 中的分层索引

  • pandas 的一项基本功能。
  • 允许在一个轴上拥有多个(两个或更多)索引级别。
  • 允许以较低维度的形式处理较高维度的数据。
  • 类比: 可以把它想象成在文件柜中,类别下有子类别。🗄️
  • 优点: 提供了一种结构化的方式来表示和操作复杂数据集。

分层索引:示例

import pandas as pd  # 导入 pandas 库,并将其简称为 pd
import numpy as np   # 导入 numpy 库,并将其简称为 np

#| echo: true
data = pd.Series(np.random.uniform(size=9),  # 创建一个包含 9 个均匀分布随机数的 Series
                 index=[["a", "a", "a", "b", "b", "c", "c", "d", "d"],  # 外层索引
                        [1, 2, 3, 1, 3, 1, 2, 2, 3]])  # 内层索引
data  # 显示 Series
  • 我们创建了一个 Series,其索引为列表的列表
  • 这将创建一个 MultiIndex 对象。
  • 索引显示中的“间隙”表示“使用正上方的标签”。

理解 MultiIndex

data.index  # 显示 Series 的索引
  • MultiIndex 对象表示分层索引。
  • 它包含 (外层, 内层) 的元组。
  • 这里:(‘a’, 1), (‘a’, 2), (‘a’, 3), (‘b’, 1) … 表示索引对。

部分索引

有了分层索引,就可以进行部分索引。 这允许简洁地选择数据子集。

data["b"]  # 选择索引为 "b" 的组

部分索引(续)

data["b":"c"]  # 选择从 "b" 到 "c" 的组(包括 "b" 和 "c")

部分索引(续)

data.loc[["b", "d"]]  # 选择索引为 "b" 和 "d" 的组

从内层选择

data.loc[:, 2]  # 选择内层索引为 2 的所有数据
  • 我们使用 .loc 进行基于标签的索引。
  • : 选择所有外层。
  • 2 选择内层索引等于 2 的数据。

unstack()stack()

  • unstack(): 将数据重塑为 DataFrame。它将(行)索引的一个级别“透视”为列标签。
  • stack(): unstack() 的逆操作。它将列标签透视为(行)MultiIndex 中的一个级别。

unstack() 示例

data.unstack()  # 将 Series 转换为 DataFrame,内层索引变为列

stack() 示例

data.unstack().stack()  # 将 DataFrame 转换回 Series,列变回内层索引

DataFrame 的分层索引

  • 行和列都可以有分层索引。
frame = pd.DataFrame(np.arange(12).reshape((4, 3)),  # 创建一个 4x3 的 DataFrame,值为 0-11
                     index=[["a", "a", "b", "b"], [1, 2, 1, 2]],  # 行索引:两层
                     columns=[["Ohio", "Ohio", "Colorado"],  # 列索引:两层
                              ["Green", "Red", "Green"]])
frame  # 显示 DataFrame
  • 这里,行和列都有两个级别。

命名索引级别

frame.index.names = ["key1", "key2"]  # 设置行索引的名称
frame.columns.names = ["state", "color"]  # 设置列索引的名称
frame  # 显示 DataFrame
  • 为索引级别命名可以提高可读性。
  • frame.index.namesframe.columns.names 设置名称。

Note

请注意,索引名称 “state” 和 “color” 不是行标签(frame.index 值)的一部分。

访问 nlevels 属性

frame.index.nlevels #查看行索引的层数
  • 您可以通过访问索引的 nlevels 属性来查看索引的层数。

部分列索引

类似于行索引,我们也可以选择列组:

frame["Ohio"]  # 选择列索引为 "Ohio" 的组

重新排序和排序级别

  • swaplevel(): 交换两个级别。
  • sort_index(): 使用索引级别对数据进行排序。可以按特定级别排序。

swaplevel() 示例

frame.swaplevel("key1", "key2")  # 交换行索引的 "key1" 和 "key2" 级别

sort_index() 示例

frame.sort_index(level=1)  # 按行索引的第 1 级(key2)排序

swaplevel()sort_index() 组合

frame.swaplevel(0, 1).sort_index(level=0)  # 先交换行索引的 0 和 1 级,然后按第 0 级排序

Note

如果索引从最外层开始按字典顺序排序,则对分层索引对象进行数据选择的性能会更好。

按级别计算汇总统计

许多描述性和汇总统计都有一个 level 选项:

frame.groupby(level="key2").sum()  # 按行索引的 "key2" 级别分组,并计算每组的和

按级别计算汇总统计(列示例)

frame.groupby(level="color", axis="columns").sum()  # 按列索引的 "color" 级别分组,并计算每组的和

使用 DataFrame 的列进行索引

  • set_index(): 使用一个或多个列作为索引创建一个新的 DataFrame。
  • reset_index(): 将分层索引级别移动到列中(与 set_index() 相反)。

示例 DataFrame

frame = pd.DataFrame({"a": range(7),  # 创建一个 DataFrame,列 "a" 的值为 0-6
                      "b": range(7, 0, -1),  # 列 "b" 的值为 7-1
                      "c": ["one", "one", "one", "two", "two",
                            "two", "two"],  # 列 "c" 的值为字符串
                      "d": [0, 1, 2, 0, 1, 2, 3]})  # 列 "d" 的值为 0-3
frame  # 显示 DataFrame

set_index() 示例

frame2 = frame.set_index(["c", "d"])  # 使用 "c" 和 "d" 列作为索引
frame2  # 显示新的 DataFrame
  • 我们使用 “c” 和 “d” 列创建一个 MultiIndex。
  • 默认情况下,用于索引的列会被删除。使用 drop=False 来保留它们。

set_index()drop=False

frame.set_index(["c", "d"], drop=False)  # 使用 "c" 和 "d" 列作为索引,并保留这些列

reset_index() 示例

frame2.reset_index()  # 将分层索引移动到列中,并创建一个默认的整数索引
  • reset_index() 将分层索引移动到列中。
  • 它创建一个默认的整数索引。

合并和拼接数据集

在 pandas 中合并数据的主要三种方法:

  1. pandas.merge: 基于(类似于 SQL 连接)连接 DataFrame 中的行。
  2. pandas.concat: 沿着轴连接或“堆叠”对象。
  3. combine_first: 拼接重叠数据(填充缺失值)。

数据库风格的 DataFrame 连接

  • pandas.merge 是连接操作的主要函数。
  • 类似于 SQL 连接。
  • 关键概念:(用于链接行的列)。

pandas.merge: 多对一连接(设置)

df1 = pd.DataFrame({"key": ["b", "b", "a", "c", "a", "a", "b"],  # DataFrame 1,包含 "key" 列
                    "data1": pd.Series(range(7), dtype="Int64")})  # 和 "data1" 列
df2 = pd.DataFrame({"key": ["a", "b", "d"],  # DataFrame 2,包含 "key" 列
                    "data2": pd.Series(range(3), dtype="Int64")})  # 和 "data2" 列
df1  # 显示 df1
df2  # 显示 df2

pandas.merge: 多对一连接(示例)

pd.merge(df1, df2)  # 合并 df1 和 df2,自动使用 "key" 列作为连接键
# 或者 pd.merge(df1, df2, on="key")  # 显式指定连接键为 "key"
  • df1 有多行标记为 ‘a’ 和 ‘b’。
  • df2 在 ‘key’ 列中每个值只有一行。
  • 如果未指定连接列,merge 将使用重叠的列名。最佳做法是显式指定。

pandas.merge: 不同的列名

如果列名不同,请分别指定它们:

df3 = pd.DataFrame({"lkey": ["b", "b", "a", "c", "a", "a", "b"],  # DataFrame 3,包含 "lkey" 列
                    "data1": pd.Series(range(7), dtype="Int64")})  # 和 "data1" 列
df4 = pd.DataFrame({"rkey": ["a", "b", "d"],  # DataFrame 4,包含 "rkey" 列
                    "data2": pd.Series(range(3), dtype="Int64")})  # 和 "data2" 列
pd.merge(df3, df4, left_on="lkey", right_on="rkey")  # 使用 "lkey" 和 "rkey" 作为连接键
  • left_on: 左侧 DataFrame 中的列。
  • right_on: 右侧 DataFrame 中的列。

pandas.merge: 连接类型

  • 默认情况下,merge 执行 “inner” 连接(键的交集)。
  • 其他连接类型:“left”, “right”, “outer”。

pandas.merge: 外连接示例

pd.merge(df1, df2, how="outer")  # 执行外连接,包含所有键
  • 外连接: 键的并集。
  • 左连接: 左侧 DataFrame 中的所有键。
  • 右连接: 右侧 DataFrame 中的所有键。

使用 how 参数的连接类型

选项 行为
how="inner" 仅使用两个表中都存在的键组合
how="left" 使用左侧表中的所有键组合
how="right" 使用右侧表中的所有键组合
how="outer" 使用两个表中一起存在的所有键组合

pandas.merge: 多对多连接

  • 多对多连接形成匹配键的笛卡尔积
df1 = pd.DataFrame({"key": ["b", "b", "a", "c", "a", "b"],  # DataFrame 1
                    "data1": pd.Series(range(6), dtype="Int64")})
df2 = pd.DataFrame({"key": ["a", "b", "a", "b", "d"],  # DataFrame 2
                    "data2": pd.Series(range(5), dtype="Int64")})

pd.merge(df1, df2, on="key", how="left")  # 左连接
  • df1 中有三个 “b” 行,df2 中有两个,因此结果中有六个 “b” 行。

使用多个键合并

传递一个列名列表:

left = pd.DataFrame({"key1": ["foo", "foo", "bar"],  # 左侧 DataFrame
                     "key2": ["one", "two", "one"],
                     "lval": pd.Series([1, 2, 3], dtype='Int64')})
right = pd.DataFrame({"key1": ["foo", "foo", "bar", "bar"],  # 右侧 DataFrame
                      "key2": ["one", "one", "one", "two"],
                      "rval": pd.Series([4, 5, 6, 7], dtype='Int64')})
pd.merge(left, right, on=["key1", "key2"], how="outer")  # 使用 "key1" 和 "key2" 作为连接键,执行外连接
  • 可以将多个键视为形成用作单个连接键的元组。

重叠的列名

  • merge 有一个 suffixes 选项来处理重叠的列名。
pd.merge(left, right, on="key1")  # 自动添加后缀 "_x" 和 "_y"

重叠的列名:suffixes

pd.merge(left, right, on="key1", suffixes=("_left", "_right"))  # 自定义后缀

pandas.merge 函数参数

参数 描述
left 左侧要合并的 DataFrame。
right 右侧要合并的 DataFrame。
how 连接类型:“inner”, “outer”, “left”, 或 “right”(默认为 “inner”)。
on 要连接的列名(必须在两个 DataFrame 中都存在)。
left_on 左侧 DataFrame 中用作连接键的列。
right_on 类似于 left_on,用于右侧 DataFrame。
left_index 使用左侧 DataFrame 的行索引作为其连接键。
right_index 类似于 left_index
sort 按连接键对合并后的数据进行字典排序(默认为 False)。
suffixes 要附加到重叠列名的字符串元组(默认为 (“_x”, “_y”))。
copy 如果为 False,则在某些情况下避免复制数据(默认为复制)。
validate 检查合并的类型(一对一,一对多,多对多)
indicator 添加名为 _merge 的列,指示每行的来源(“left_only”, “right_only”, “both”)。

基于索引合并

  • 使用 left_index=Trueright_index=True(或两者)来基于索引合并。
left1 = pd.DataFrame({"key": ["a", "b", "a", "a", "b", "c"],  # 左侧 DataFrame
                      "value": pd.Series(range(6), dtype="Int64")})
right1 = pd.DataFrame({"group_val": [3.5, 7]}, index=["a", "b"])  # 右侧 DataFrame,使用索引

pd.merge(left1, right1, left_on="key", right_index=True)  # 将 left1 的 "key" 列与 right1 的索引合并
  • 我们将 left1 的 “key” 列与 right1 的索引合并。
  • left1的索引会被保留。

分层索引:多键合并

对于分层索引,基于索引连接类似于多键合并:

lefth = pd.DataFrame({"key1": ["Ohio", "Ohio", "Ohio",
                            "Nevada", "Nevada"],  # 左侧 DataFrame
                    "key2": [2000, 2001, 2002, 2001, 2002],
                    "data": pd.Series(range(5), dtype="Int64")})
righth_index = pd.MultiIndex.from_arrays([  # 右侧 DataFrame 的 MultiIndex
    ["Nevada", "Nevada", "Ohio", "Ohio", "Ohio", "Ohio"],
    [2001, 2000, 2000, 2000, 2001, 2002]
    ])
righth = pd.DataFrame({"event1": pd.Series([0, 2, 4, 6, 8, 10], dtype="Int64",
                                        index=righth_index),  # 使用 MultiIndex
                    "event2": pd.Series([1, 3, 5, 7, 9, 11], dtype="Int64",
                                        index=righth_index)})

pd.merge(lefth, righth, left_on=["key1", "key2"], right_index=True, how="outer")  # 基于多个列和索引合并

DataFrame 的 join 方法

  • 简化了基于索引的合并。
  • 默认执行左连接
left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
                     index=["a", "c", "e"],  # 左侧 DataFrame
                     columns=["Ohio", "Nevada"]).astype("Int64")
right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
                      index=["b", "c", "d", "e"],  # 右侧 DataFrame
                      columns=["Missouri", "Alabama"]).astype("Int64")
left2.join(right2, how="outer")  # 基于索引连接
  • 可以基于调用 DataFrame 的其中一列进行连接。
  • 支持连接具有相似索引但列不重叠的多个 DataFrame。

join 基于列

left1.join(right1, on="key")  # 将 left1 与 right1 基于 left1 的 "key" 列连接

沿轴连接

  • numpy.concatenate: 适用于 NumPy 数组。
arr = np.arange(12).reshape((3, 4))  # 创建一个 3x4 的数组
np.concatenate([arr, arr], axis=1)  # 沿着列连接
  • pandas.concat: 解决以下问题:
    • 处理不同的索引。
    • 识别连接的块。
    • 保留数据。

pandas.concat 与 Series

s1 = pd.Series([0, 1], index=["a", "b"], dtype="Int64")  # Series 1
s2 = pd.Series([2, 3, 4], index=["c", "d", "e"], dtype="Int64")  # Series 2
s3 = pd.Series([5, 6], index=["f", "g"], dtype="Int64")  # Series 3
pd.concat([s1, s2, s3])  # 沿着行连接 Series
  • 默认情况下,concat 沿着 axis="index"(行)工作,生成另一个 Series。
  • 将值和索引粘合在一起。

pandas.concat: axis="columns"

pd.concat([s1, s2, s3], axis="columns")  # 沿着列连接 Series,生成 DataFrame
  • axis="columns" 生成一个 DataFrame。
  • 结果是索引的并集(外连接)。

pandas.concat: join="inner"

s4 = pd.concat([s1, s3])  # 连接 s1 和 s3
pd.concat([s1, s4], axis="columns", join="inner")  # 沿着列连接 s1 和 s4,执行内连接
  • join="inner" 对索引执行交集。

pandas.concat: keys 参数

result = pd.concat([s1, s1, s3], keys=["one", "two", "three"])  # 连接并创建分层索引
result  # 显示结果

unstack() 结果

result.unstack()  # 将结果转换为 DataFrame
  • keys 创建一个分层索引。标识连接的块。
  • 当沿着 axis="columns" 连接时,keys 成为 DataFrame 列标题。

pandas.concat 与 DataFrame

逻辑与 Series 相同:

df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=["a", "b", "c"],  # DataFrame 1
                   columns=["one", "two"])
df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=["a", "c"],  # DataFrame 2
                   columns=["three", "four"])
pd.concat([df1, df2], axis="columns", keys=["level1", "level2"])  # 沿着列连接,并使用 keys 作为列标题
  • 您可以使用 names 参数命名创建的轴级别。
  • 如果行索引不包含相关数据,请使用 ignore_index=True

pandas.concat 函数参数

参数 描述
objs 要连接的 pandas 对象列表或字典(必需)。
axis 要连接的轴(默认为 “index”)。
join “inner” 或 “outer”(默认为 “outer”)。
keys 与要连接的对象关联的值,形成一个分层索引。
levels 用作分层索引级别的特定索引。
names 创建的分层级别的名称。
verify_integrity 检查新轴是否有重复项,如果有则引发异常(默认为 False)。
ignore_index 不保留索引;生成一个新的 range(total_length) 索引。

合并重叠数据

  • numpy.where: 执行面向数组的 if-else 操作。
a = pd.Series([np.nan, 2.5, 0.0, 3.5, 4.5, np.nan],  # Series a,包含 NaN 值
              index=["f", "e", "d", "c", "b", "a"])
b = pd.Series([0., np.nan, 2., np.nan, np.nan, 5.],  # Series b,包含 NaN 值
              index=["a", "b", "c", "d", "e", "f"])
np.where(pd.isna(a), b, a)  # 如果 a 中有 NaN,则使用 b 中的值,否则使用 a 中的值

Series.combine_first

  • Series.combine_first: 按索引对齐值并“修补”缺失数据。
a.combine_first(b)  # 使用 b 中的值填充 a 中的 NaN 值,按索引对齐
  • combine_first 按索引对齐(与 np.where 不同)。

combine_first 与 DataFrame

  • combine_first 逐列工作。
  • 它使用传递对象中的数据“修补”调用对象中的缺失数据。
df1 = pd.DataFrame({"a": [1., np.nan, 5., np.nan],  # DataFrame 1,包含 NaN 值
                    "b": [np.nan, 2., np.nan, 6.],
                    "c": range(2, 18, 4)})
df2 = pd.DataFrame({"a": [5., 4., np.nan, 3., 7.],  # DataFrame 2,包含 NaN 值
                    "b": [np.nan, 3., 4., 6., 8.]})
df1.combine_first(df2)  # 使用 df2 中的值填充 df1 中的 NaN 值,逐列进行

重塑和透视

  • 重塑/透视操作: 重新排列表格数据。
  • 分层索引 提供了一种一致的方式来重塑。
  • 两个主要操作:
    • stack: 将列“旋转”或透视到行。
    • unstack: 将行透视到列。

stackunstack: 示例 DataFrame

data = pd.DataFrame(np.arange(6).reshape((2, 3)),  # 创建一个 2x3 的 DataFrame
                    index=pd.Index(["Ohio", "Colorado"], name="state"),  # 行索引,名为 "state"
                    columns=pd.Index(["one", "two", "three"],
                    name="number"))  # 列索引,名为 "number"
data  # 显示 DataFrame

stack 示例

result = data.stack()  # 将 DataFrame 转换为 Series,列索引变为内层行索引
result  # 显示 Series

unstack 示例

result.unstack()  # 将 Series 转换回 DataFrame,内层行索引变为列索引

unstack 与不同的级别

  • 默认情况下,最内层被 unstacked。
  • 通过数字或名称指定不同的级别。
result.unstack(level=0)  # 将外层行索引("state")unstack 到列
# 或 result.unstack("state")  # 使用名称指定要 unstack 的级别
  • Unstacking 可能会引入缺失数据。
  • Stacking 默认会过滤掉缺失数据。

Stacking 与 dropna=False

s1 = pd.Series([0, 1, 2, 3], index=["a", "b", "c", "d"], dtype="Int64")  # Series 1
s2 = pd.Series([4, 5, 6], index=["c", "d", "e"], dtype="Int64")  # Series 2
data2 = pd.concat([s1, s2], keys=["one", "two"])  # 连接 s1 和 s2,并创建分层索引
data2.unstack().stack(dropna=False)  # 先 unstack,再 stack,并保留 NaN 值

DataFrame 中的 unstack

在 DataFrame 中 unstacking 时,unstacked 的级别将成为最低级别。

df = pd.DataFrame({"left": result, "right": result + 5},  # 创建一个 DataFrame
                  columns=pd.Index(["left", "right"], name="side"))  # 列索引,名为 "side"
df  # 显示 DataFrame

unstackstack 示例

df.unstack(level="state").stack(level="side")  # 先按 "state" 级别 unstack,再按 "side" 级别 stack

将“长”格式透视为“宽”格式

  • 长/堆叠格式: 常用于存储多个时间序列。每行是一个观察值。
  • 宽格式: 每个变量都有自己的列。

示例:长格式数据

data = pd.read_csv("examples/macrodata.csv")  # 从 CSV 文件读取数据
data = data.loc[:, ["year", "quarter", "realgdp", "infl", "unemp"]]  # 选择特定的列
periods = pd.PeriodIndex(year=data.pop("year"),  # 创建一个 PeriodIndex
                        quarter=data.pop("quarter"),
                        name="date")
data.index = periods.to_timestamp("D")  # 将 PeriodIndex 转换为 Timestamp,并设置为索引
data = data.reindex(columns=["realgdp", "infl", "unemp"])  # 重新设置列的顺序
data.columns.name = "item"  # 设置列索引的名称
long_data = (data.stack()  # 将 DataFrame 转换为 Series
                .reset_index()  # 重置索引
                .rename(columns={0: "value"}))  # 重命名列
long_data[:10]  # 显示前 10 行

pivot 方法

pivot 方法将长格式转换为宽格式。

pivoted = long_data.pivot(index="date", columns="item",
                          values="value")  # 将长格式数据透视为宽格式
pivoted.head()  # 显示前几行
  • index: 用作行索引的列。
  • columns: 用于创建新列的列。
  • values: 用于填充 DataFrame 的列。

使用多个值列进行透视

long_data["value2"] = np.random.standard_normal(len(long_data))  # 添加一个新的值列
pivoted = long_data.pivot(index="date", columns="item")  # 透视,不指定 values 参数
pivoted.head()  # 显示前几行,现在有分层列
  • 如果省略 values 参数,则会得到分层列。

pivot 等价于…

pivot 等价于使用 set_index 后跟 unstack

unstacked = long_data.set_index(["date", "item"]).unstack(level="item")  # 先设置索引,再 unstack
unstacked.head()  # 显示前几行

将“宽”格式透视为“长”格式

  • pandas.melt: pivot 的逆操作。将多列合并为一列(更长的 DataFrame)。
df = pd.DataFrame({"key": ["foo", "bar", "baz"],  # 创建一个 DataFrame
                   "A": [1, 2, 3],
                   "B": [4, 5, 6],
                   "C": [7, 8, 9]})
melted = pd.melt(df, id_vars="key")  # 将 DataFrame 从宽格式转换为长格式
melted  # 显示结果
  • id_vars: 组指示器列。
  • value_vars: 要“取消透视”的列。如果未指定,则使用所有其他列。

pandas.melt 示例

pd.melt(df, id_vars="key", value_vars=["A", "B"])  # 指定要取消透视的列
pd.melt(df, value_vars=["A", "B", "C"])  # 不指定组指示器列
pd.melt(df, value_vars=["key", "A", "B"])  # `value_vars` 也可以包含组标识符
  • 可以不使用任何组标识符。

使用 meltpivot 重塑

reshaped = melted.pivot(index="key", columns="variable",
                        values="value")  # 使用 pivot 将数据恢复到类似原始布局
reshaped  # 显示结果
reshaped.reset_index()  # 将索引移回列中
  • pivot 可以重塑回原始布局。
  • 由于 pivot 会创建一个索引,因此 reset_index() 可能很有用。

总结 📝

  • 我们介绍了 pandas 中数据整理的关键技术:
    • 分层索引 (MultiIndex)。
    • merge (数据库风格的连接)。
    • concat (沿轴连接)。
    • combine_first (修补缺失数据)。
    • stackunstack (重塑)。
    • pivot (长格式到宽格式)。
    • melt (宽格式到长格式)。
  • 这些工具对于为分析准备数据至关重要。

思考与讨论 🤔

  • 如何在您自己的数据分析项目中应用这些技术?
  • 您能想到现实世界的例子吗?
  • 有哪些挑战或限制?
  • 您最有兴趣应用哪种方法,为什么?
  • 在什么情况下您会更喜欢 merge 而不是 concat,反之亦然?
  • 理解分层索引如何改进复杂数据集的结构化?