Pandas 2 使用指南:合并、连接、串联和比较


pandas 提供了多种方法来合并和比较 SeriesDataFrame

concat()

concat() 函数沿着轴将任意数量的 SeriesDataFrame 对象串联在一起,并在其他轴上执行可选的集合逻辑(并集或交集)。与 numpy.concatenate 类似,concat() 接受一个同类型对象的列表或字典,并将它们串联在一起。

df1 = pd.DataFrame(
    {
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
        "C": ["C0", "C1", "C2", "C3"],
        "D": ["D0", "D1", "D2", "D3"],
    },
    index=[0, 1, 2, 3],
)


df2 = pd.DataFrame(
    {
        "A": ["A4", "A5", "A6", "A7"],
        "B": ["B4", "B5", "B6", "B7"],
        "C": ["C4", "C5", "C6", "C7"],
        "D": ["D4", "D5", "D6", "D7"],
    },
    index=[4, 5, 6, 7],
)


df3 = pd.DataFrame(
    {
        "A": ["A8", "A9", "A10", "A11"],
        "B": ["B8", "B9", "B10", "B11"],
        "C": ["C8", "C9", "C10", "C11"],
        "D": ["D8", "D9", "D10", "D11"],
    },
    index=[8, 9, 10, 11],
)


frames = [df1, df2, df3]

result = pd.concat(frames)

result
Out[6]: 
      A    B    C    D
0    A0   B0   C0   D0
1    A1   B1   C1   D1
2    A2   B2   C2   D2
3    A3   B3   C3   D3
4    A4   B4   C4   D4
5    A5   B5   C5   D5
6    A6   B6   C6   D6
7    A7   B7   C7   D7
8    A8   B8   C8   D8
9    A9   B9   C9   D9
10  A10  B10  C10  D10
11  A11  B11  C11  D11

../_images/merging_concat_basic.png

注意

concat() 会完全复制数据,并且反复使用 concat() 可能会创建不必要的副本。在使用 concat() 之前,将所有的 DataFrameSeries 对象收集到一个列表中。

frames = [process_your_file(f) for f in files]
result = pd.concat(frames)

注意

当串联具有命名轴的 DataFrame 时,pandas 会尽可能保留这些索引/列名。如果所有输入共享一个公共名称,该名称将分配给结果。当输入名称不完全一致时,结果将没有名称。对于 MultiIndex 也是如此,但逻辑是在逐级基础上分别应用的。

连接轴的连接逻辑

join 关键字指定如何处理第一个DataFrame 中不存在的轴值。

join='outer' 取所有轴值的并集

df4 = pd.DataFrame(
    {
        "B": ["B2", "B3", "B6", "B7"],
        "D": ["D2", "D3", "D6", "D7"],
        "F": ["F2", "F3", "F6", "F7"],
    },
    index=[2, 3, 6, 7],
)


result = pd.concat([df1, df4], axis=1)

result
Out[9]: 
     A    B    C    D    B    D    F
0   A0   B0   C0   D0  NaN  NaN  NaN
1   A1   B1   C1   D1  NaN  NaN  NaN
2   A2   B2   C2   D2   B2   D2   F2
3   A3   B3   C3   D3   B3   D3   F3
6  NaN  NaN  NaN  NaN   B6   D6   F6
7  NaN  NaN  NaN  NaN   B7   D7   F7

../_images/merging_concat_axis1.png

join='inner' 取所有轴值的交集

result = pd.concat([df1, df4], axis=1, join="inner")

result
Out[11]: 
    A   B   C   D   B   D   F
2  A2  B2  C2  D2  B2  D2  F2
3  A3  B3  C3  D3  B3  D3  F3

../_images/merging_concat_axis1_inner.png

为了使用原始DataFrame精确索引执行有效的“左连接”,可以重新索引结果。

result = pd.concat([df1, df4], axis=1).reindex(df1.index)

result
Out[13]: 
    A   B   C   D    B    D    F
0  A0  B0  C0  D0  NaN  NaN  NaN
1  A1  B1  C1  D1  NaN  NaN  NaN
2  A2  B2  C2  D2   B2   D2   F2
3  A3  B3  C3  D3   B3   D3   F3

../_images/merging_concat_axis1_join_axes.png

忽略连接轴上的索引

对于没有有意义索引的 DataFrame 对象,ignore_index 可以忽略重叠的索引。

result = pd.concat([df1, df4], ignore_index=True, sort=False)

result
Out[15]: 
     A   B    C   D    F
0   A0  B0   C0  D0  NaN
1   A1  B1   C1  D1  NaN
2   A2  B2   C2  D2  NaN
3   A3  B3   C3  D3  NaN
4  NaN  B2  NaN  D2   F2
5  NaN  B3  NaN  D3   F3
6  NaN  B6  NaN  D6   F6
7  NaN  B7  NaN  D7   F7

../_images/merging_concat_ignore_index.png

SeriesDataFrame 连接在一起

可以将混合的 SeriesDataFrame 对象连接在一起。Series 将被转换为具有 Series 名称作为列名的 DataFrame

s1 = pd.Series(["X0", "X1", "X2", "X3"], name="X")

result = pd.concat([df1, s1], axis=1)

result
Out[18]: 
    A   B   C   D   X
0  A0  B0  C0  D0  X0
1  A1  B1  C1  D1  X1
2  A2  B2  C2  D2  X2
3  A3  B3  C3  D3  X3

../_images/merging_concat_mixed_ndim.png

无名称的 Series 将按顺序编号。

s2 = pd.Series(["_0", "_1", "_2", "_3"])

result = pd.concat([df1, s2, s2, s2], axis=1)

result
Out[21]: 
    A   B   C   D   0   1   2
0  A0  B0  C0  D0  _0  _0  _0
1  A1  B1  C1  D1  _1  _1  _1
2  A2  B2  C2  D2  _2  _2  _2
3  A3  B3  C3  D3  _3  _3  _3

ignore_index=True 会丢弃所有的名称引用。

result = pd.concat([df1, s1], axis=1, ignore_index=True)

result
Out[23]: 
    0   1   2   3   4
0  A0  B0  C0  D0  X0
1  A1  B1  C1  D1  X1
2  A2  B2  C2  D2  X2
3  A3  B3  C3  D3  X3

../_images/merging_concat_series_ignore_index.png

结果的 keys

keys 参数会在结果的索引或列上添加另一个轴级别(创建一个 MultiIndex),将特定的键与每个原始 DataFrame 关联起来。

result = pd.concat(frames, keys=["x", "y", "z"])

result
Out[25]: 
        A    B    C    D
x 0    A0   B0   C0   D0
  1    A1   B1   C1   D1
  2    A2   B2   C2   D2
  3    A3   B3   C3   D3
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11

result.loc["y"]
Out[26]: 
    A   B   C   D
4  A4  B4  C4  D4
5  A5  B5  C5  D5
6  A6  B6  C6  D6
7  A7  B7  C7  D7

../_images/merging_concat_keys.png

当创建一个基于现有 Series 的新 DataFrame 时,keys 参数可以覆盖列名。

s3 = pd.Series([0, 1, 2, 3], name="foo")

s4 = pd.Series([0, 1, 2, 3])

s5 = pd.Series([0, 1, 4, 5])

pd.concat([s3, s4, s5], axis=1)
Out[30]: 
   foo  0  1
0    0  0  0
1    1  1  1
2    2  2  4
3    3  3  5

pd.concat([s3, s4, s5], axis=1, keys=["red", "blue", "yellow"])
Out[31]: 
   red  blue  yellow
0    0     0       0
1    1     1       1
2    2     2       4
3    3     3       5

你还可以将一个字典传递给 concat(),在这种情况下,字典的键将用于 keys 参数,除非指定了其他的 keys 参数:

pieces = {"x": df1, "y": df2, "z": df3}

result = pd.concat(pieces)

result
Out[34]: 
        A    B    C    D
x 0    A0   B0   C0   D0
  1    A1   B1   C1   D1
  2    A2   B2   C2   D2
  3    A3   B3   C3   D3
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11

../_images/merging_concat_dict.png

result = pd.concat(pieces, keys=["z", "y"])

result
Out[36]: 
        A    B    C    D
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7

../_images/merging_concat_dict_keys.png

MultiIndex 的级别是根据传递的键和 DataFrame 片段的索引构建的:

result.index.levels
Out[37]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])

levels 参数允许指定与 keys 关联的结果级别:

result = pd.concat(
    pieces, keys=["x", "y", "z"], levels=[["z", "y", "x", "w"]], names=["group_key"]
)

result
Out[39]: 
                A    B    C    D
group_key                       
x         0    A0   B0   C0   D0
          1    A1   B1   C1   D1
          2    A2   B2   C2   D2
          3    A3   B3   C3   D3
y         4    A4   B4   C4   D4
          5    A5   B5   C5   D5
          6    A6   B6   C6   D6
          7    A7   B7   C7   D7
z         8    A8   B8   C8   D8
          9    A9   B9   C9   D9
          10  A10  B10  C10  D10
          11  A11  B11  C11  D11

../_images/merging_concat_dict_keys_names.png

result.index.levels
Out[40]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])

将行附加到 DataFrame

如果你有一个要作为单行附加到 DataFrameSeries,你可以将该行转换为 DataFrame,然后使用 concat()

s2 = pd.Series(["X0", "X1", "X2", "X3"], index=["A", "B", "C", "D"])

result = pd.concat([df1, s2.to_frame().T], ignore_index=True)

result
Out[43]: 
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3
4  X0  X1  X2  X3

../_images/merging_append_series_as_row.png

merge()

merge() 执行类似于 SQL 的连接操作。熟悉 SQL 但是对 pandas 新手的用户可以参考 与 SQL 的比较

连接类型

merge() 实现了常见的 SQL 风格的连接操作。

  • one-to-one:在它们的索引上连接两个 DataFrame 对象,索引必须包含唯一值。
  • many-to-one:将唯一索引连接到不同 DataFrame 中的一个或多个列。
  • many-to-many:在列上连接列。

注意

当在列上连接列时,可能是多对多连接,传递的 DataFrame 对象上的任何索引都将被丢弃。

对于多对多连接,如果一个键组合在两个表中都出现了多次,那么 DataFrame 将具有相关数据的笛卡尔积

left = pd.DataFrame(
    {
        "key": ["K0", "K1", "K2", "K3"],
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
    }
)


right = pd.DataFrame(
    {
        "key": ["K0", "K1", "K2", "K3"],
        "C": ["C0", "C1", "C2", "C3"],
        "D": ["D0", "D1", "D2", "D3"],
    }
)


result = pd.merge(left, right, on="key")

result
Out[47]: 
  key   A   B   C   D
0  K0  A0  B0  C0  D0
1  K1  A1  B1  C1  D1
2  K2  A2  B2  C2  D2
3  K3  A3  B3  C3  D3

../_images/merging_merge_on_key.png

how 参数用于指定哪些键包含在结果表中。如果一个键组合在左表或右表中都没有出现,那么连接表中的值将为 NA。下面是 how 选项及其 SQL 等效名称的摘要:

连接方法SQL 连接名称描述
leftLEFT OUTERJOIN仅使用左侧表的键
rightRIGHT OUTERJOIN仅使用右侧表的键
outerFULL OUTERJOIN使用左右两侧表的键的并集
innerINNER JOIN使用左右两侧表的键的交集
crossCROSS JOIN创建左右两侧表行的笛卡尔积
left = pd.DataFrame(
   {
      "key1": ["K0", "K0", "K1", "K2"],
      "key2": ["K0", "K1", "K0", "K1"],
      "A": ["A0", "A1", "A2", "A3"],
      "B": ["B0", "B1", "B2", "B3"],
   }
)


right = pd.DataFrame(
   {
      "key1": ["K0", "K1", "K1", "K2"],
      "key2": ["K0", "K0", "K0", "K0"],
      "C": ["C0", "C1", "C2", "C3"],
      "D": ["D0", "D1", "D2", "D3"],
   }
)


result = pd.merge(left, right, how="left", on=["key1", "key2"])

result
Out[51]: 
  key1 key2   A   B    C    D
0   K0   K0  A0  B0   C0   D0
1   K0   K1  A1  B1  NaN  NaN
2   K1   K0  A2  B2   C1   D1
3   K1   K0  A2  B2   C2   D2
4   K2   K1  A3  B3  NaN  NaN

../_images/merging_merge_on_key_left.png

result = pd.merge(left, right, how="right", on=["key1", "key2"])

result
Out[53]: 
  key1 key2    A    B   C   D
0   K0   K0   A0   B0  C0  D0
1   K1   K0   A2   B2  C1  D1
2   K1   K0   A2   B2  C2  D2
3   K2   K0  NaN  NaN  C3  D3

../_images/merging_merge_on_key_right.png

result = pd.merge(left, right, how="outer", on=["key1", "key2"])

result
Out[55]: 
  key1 key2    A    B    C    D
0   K0   K0   A0   B0   C0   D0
1   K0   K1   A1   B1  NaN  NaN
2   K1   K0   A2   B2   C1   D1
3   K1   K0   A2   B2   C2   D2
4   K2   K0  NaN  NaN   C3   D3
5   K2   K1   A3   B3  NaN  NaN

你还可以将一个字典传递给 concat(),在这种情况下,字典的键将用于 keys 参数,除非指定了其他的 keys 参数:

pieces = {"x": df1, "y": df2, "z": df3}

result = pd.concat(pieces)

result
Out[34]: 
        A    B    C    D
x 0    A0   B0   C0   D0
  1    A1   B1   C1   D1
  2    A2   B2   C2   D2
  3    A3   B3   C3   D3
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11

../_images/merging_concat_dict.png

result = pd.concat(pieces, keys=["z", "y"])

result
Out[36]: 
        A    B    C    D
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7

../_images/merging_concat_dict_keys.png

MultiIndex 创建的级别是根据传递的键和 DataFrame 片段的索引构建的:

result.index.levels
Out[37]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])

levels 参数允许指定与 keys 关联的结果级别:

result = pd.concat(
    pieces, keys=["x", "y", "z"], levels=[["z", "y", "x", "w"]], names=["group_key"]
)

result
Out[39]: 
                A    B    C    D
group_key                       
x         0    A0   B0   C0   D0
          1    A1   B1   C1   D1
          2    A2   B2   C2   D2
          3    A3   B3   C3   D3
y         4    A4   B4   C4   D4
          5    A5   B5   C5   D5
          6    A6   B6   C6   D6
          7    A7   B7   C7   D7
z         8    A8   B8   C8   D8
          9    A9   B9   C9   D9
          10  A10  B10  C10  D10
          11  A11  B11  C11  D11

../_images/merging_concat_dict_keys_names.png

result.index.levels
Out[40]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])

将行附加到 DataFrame

如果你有一个要作为单行附加到 DataFrameSeries,你可以将该行转换为 DataFrame,然后使用 concat()

s2 = pd.Series(["X0", "X1", "X2", "X3"], index=["A", "B", "C", "D"])

result = pd.concat([df1, s2.to_frame().T], ignore_index=True)

result
Out[43]: 
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3
4  X0  X1  X2  X3

../_images/merging_append_series_as_row.png

merge()

merge() 执行类似于 SQL 的连接操作。熟悉 SQL 但是对 pandas 新手的用户可以参考 与 SQL 的比较

连接类型

merge() 实现了常见的 SQL 风格的连接操作。

  • one-to-one:在它们的索引上连接两个 DataFrame 对象,索引必须包含唯一值。
  • many-to-one:将唯一索引连接到不同 DataFrame 中的一个或多个列。
  • many-to-many:在列上连接列。

注意

当在列上连接列时,可能是多对多连接,传递的 DataFrame 对象上的任何索引都将被丢弃。

对于多对多连接,如果一个键组合在两个表中都出现了多次,那么 DataFrame 将具有相关数据的笛卡尔积

left = pd.DataFrame(
    {
        "key": ["K0", "K1", "K2", "K3"],
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
    }
)


right = pd.DataFrame(
    {
        "key": ["K0", "K1", "K2", "K3"],
        "C": ["C0", "C1", "C2", "C3"],
        "D": ["D0", "D1", "D2", "D3"],
    }
)


result = pd.merge(left, right, on="key")

result
Out[47]: 
  key   A   B   C   D
0  K0  A0  B0  C0  D0
1  K1  A1  B1  C1  D1
2  K2  A2  B2  C2  D2
3  K3  A3  B3  C3  D3

../_images/merging_merge_on_key.png

how 参数用于指定哪些键包含在结果表中。如果一个键组合在左表或右表中都没有出现,那么连接表中的值将为 NA。下面是 how 选项及其 SQL 等效名称的摘要:

连接方法SQL 连接名称描述
leftLEFT OUTERJOIN仅使用左侧表的键
rightRIGHT OUTERJOIN仅使用右侧表的键
outerFULL OUTERJOIN使用左右两侧表的键的并集
innerINNER JOIN使用左右两侧表的键的交集
crossCROSS JOIN创建左右两侧表行的笛卡尔积
left = pd.DataFrame(
   {
      "key1": ["K0", "K0", "K1", "K2"],
      "key2": ["K0", "K1", "K0", "K1"],
      "A": ["A0", "A1", "A2", "A3"],
      "B": ["B0", "B1", "B2", "B3"],
   }
)


right = pd.DataFrame(
   {
      "key1": ["K0", "K1", "K1", "K2"],
      "key2": ["K0", "K0", "K0", "K0"],
      "C": ["C0", "C1", "C2", "C3"],
      "D": ["D0", "D1", "D2", "D3"],
   }
)


result = pd.merge(left, right, how="left", on=["key1", "key2"])

result
Out[51]: 
  key1 key2   A   B    C    D
0   K0   K0  A0  B0   C0   D0
1   K0   K1  A1  B1  NaN  NaN
2   K1   K0  A2  B2   C1   D1
3   K1   K0  A2  B2   C2   D2
4   K2   K1  A3  B3  NaN  NaN

../_images/merging_merge_on_key_left.png

result = pd.merge(left, right, how="right", on=["key1", "key2"])

result
Out[53]: 
  key1 key2    A    B   C   D
0   K0   K0   A0   B0  C0  D0
1   K1   K0   A2   B2  C1  D1
2   K1   K0   A2   B2  C2  D2
3   K2   K0  NaN  NaN  C3  D3

../_images/merging_merge_on_key_right.png

result = pd.merge(left, right, how="outer", on=["key1", "key2"])

result
Out[55]: 
  key1 key2    A    B    C    D
0   K0   K0   A0   B0   C0   D0
1   K0   K1   A1   B1  NaN  NaN
2   K1   K0   A2   B2   C1   D1
3   K1   K0   A2   B2   C2   D2
4   K2   K0  NaN  NaN   C3   D3
5   K2   K1   A3   B3  NaN  NaN

你还可以将一个字典传递给 concat(),在这种情况下,字典的键将用于 keys 参数,除非指定了其他的 keys 参数:

pieces = {"x": df1, "y": df2, "z": df3}

result = pd.concat(pieces)

result
Out[34]: 
        A    B    C    D
x 0    A0   B0   C0   D0
  1    A1   B1   C1   D1
  2    A2   B2   C2   D2
  3    A3   B3   C3   D3
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11

../_images/merging_concat_dict.png

result = pd.concat(pieces, keys=["z", "y"])

result
Out[36]: 
        A    B    C    D
z 8    A8   B8   C8   D8
  9    A9   B9   C9   D9
  10  A10  B10  C10  D10
  11  A11  B11  C11  D11
y 4    A4   B4   C4   D4
  5    A5   B5   C5   D5
  6    A6   B6   C6   D6
  7    A7   B7   C7   D7

../_images/merging_concat_dict_keys.png

MultiIndex 创建的级别是根据传递的键和 DataFrame 片段的索引构建的:

result.index.levels
Out[37]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])

levels 参数允许指定与 keys 关联的结果级别:

result = pd.concat(
    pieces, keys=["x", "y", "z"], levels=[["z", "y", "x", "w"]], names=["group_key"]
)

result
Out[39]: 
                A    B    C    D
group_key                       
x         0    A0   B0   C0   D0
          1    A1   B1   C1   D1
          2    A2   B2   C2   D2
          3    A3   B3   C3   D3
y         4    A4   B4   C4   D4
          5    A5   B5   C5   D5
          6    A6   B6   C6   D6
          7    A7   B7   C7   D7
z         8    A8   B8   C8   D8
          9    A9   B9   C9   D9
          10  A10  B10  C10  D10
          11  A11  B11  C11  D11

../_images/merging_concat_dict_keys_names.png

result.index.levels
Out[40]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])
result = pd.merge(left, right, how="inner", on=["key1", "key2"])

result
Out[57]: 
  key1 key2   A   B   C   D
0   K0   K0  A0  B0  C0  D0
1   K1   K0  A2  B2  C1  D1
2   K1   K0  A2  B2  C2  D2

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行内连接。内连接是指只保留两个 DataFrame 中共有的键值对,其他的键值对将被丢弃。在这个例子中,我们使用了 key1key2 作为连接的键。连接后的结果存储在变量 result 中。

result = pd.merge(left, right, how="cross")

result
Out[59]: 
   key1_x key2_x   A   B key1_y key2_y   C   D
0      K0     K0  A0  B0     K0     K0  C0  D0
1      K0     K0  A0  B0     K1     K0  C1  D1
2      K0     K0  A0  B0     K1     K0  C2  D2
3      K0     K0  A0  B0     K2     K0  C3  D3
4      K0     K1  A1  B1     K0     K0  C0  D0
..    ...    ...  ..  ..    ...    ...  ..  ..
11     K1     K0  A2  B2     K2     K0  C3  D3
12     K2     K1  A3  B3     K0     K0  C0  D0
13     K2     K1  A3  B3     K1     K0  C1  D1
14     K2     K1  A3  B3     K1     K0  C2  D2
15     K2     K1  A3  B3     K2     K0  C3  D3

[16 rows x 8 columns]

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行交叉连接。交叉连接是指将两个 DataFrame 中的每一行都与另一个 DataFrame 中的每一行进行连接,生成的结果 DataFrame 的行数为两个 DataFrame 的行数的乘积。连接后的结果存储在变量 result 中。

df = pd.DataFrame({"Let": ["A", "B", "C"], "Num": [1, 2, 3]})

df
Out[61]: 
  Let  Num
0   A    1
1   B    2
2   C    3

ser = pd.Series(
    ["a", "b", "c", "d", "e", "f"],
    index=pd.MultiIndex.from_arrays(
        [["A", "B", "C"] * 2, [1, 2, 3, 4, 5, 6]], names=["Let", "Num"]
    ),
)


ser
Out[63]: 
Let  Num
A    1      a
B    2      b
C    3      c
A    4      d
B    5      e
C    6      f
dtype: object

pd.merge(df, ser.reset_index(), on=["Let", "Num"])
Out[64]: 
  Let  Num  0
0   A    1  a
1   B    2  b
2   C    3  c

在这个例子中,我们使用了 pd.merge() 函数将 DataFrame df 和 Series ser 进行连接。连接的键是 LetNum。连接后的结果存储在变量 result 中。

left = pd.DataFrame({"A": [1, 2], "B": [2, 2]})

right = pd.DataFrame({"A": [4, 5, 6], "B": [2, 2, 2]})

result = pd.merge(left, right, on="B", how="outer")

result
Out[68]: 
   A_x  B  A_y
0    1  2    4
1    1  2    5
2    1  2    6
3    2  2    4
4    2  2    5
5    2  2    6

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行外连接。外连接是指保留两个 DataFrame 中所有的键值对,如果某个键在其中一个 DataFrame 中不存在,则用 NaN 填充。连接后的结果存储在变量 result 中。

警告:在进行外连接时,如果连接的键存在重复值,会导致结果的维度显著增加,可能会导致内存溢出。

left = pd.DataFrame({"A": [1, 2], "B": [1, 2]})

right = pd.DataFrame({"A": [4, 5, 6], "B": [2, 2, 2]})

result = pd.merge(left, right, on="B", how="outer", validate="one_to_one")
---------------------------------------------------------------------------
MergeError                                Traceback (most recent call last)
Cell In[71], line 1
----> 1 result = pd.merge(left, right, on="B", how="outer", validate="one_to_one")

...

MergeError: Merge keys are not unique in right dataset; not a one-to-one merge

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行外连接,并使用 validate="one_to_one" 参数进行验证。由于右侧 DataFrame 中的连接键存在重复值,所以验证失败,抛出了 MergeError 异常。

如果用户知道右侧 DataFrame 中存在重复值,但希望确保左侧 DataFrame 中没有重复值,可以使用 validate='one_to_many' 参数,这样就不会抛出异常。

pd.merge(left, right, on="B", how="outer", validate="one_to_many")
Out[72]: 
   A_x  B  A_y
0    1  1  NaN
1    2  2  4.0
2    2  2  5.0
3    2  2  6.0

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行外连接,并使用 validate="one_to_many" 参数进行验证。由于左侧 DataFrame 中的连接键不存在重复值,所以验证通过,连接后的结果存储在变量 result 中。

merge() 函数接受一个 indicator 参数。如果设置为 True,则会在输出对象中添加一个名为 _merge 的分类列,其取值如下:

  • 只在 'left' DataFrame 中存在连接键:left_only
  • 只在 'right' DataFrame 中存在连接键:right_only
  • 在两个 DataFrame 中都存在连接键:both
df1 = pd.DataFrame({"col1": [0, 1], "col_left": ["a", "b"]})

df2 = pd.DataFrame({"col1": [1, 2, 2], "col_right": [2, 2, 2]})

pd.merge(df1, df2, on="col1", how="outer", indicator=True)
Out[75]: 
   col1 col_left  col_right      _merge
0     0        a        NaN   left_only
1     1        b        2.0        both
2     2      NaN        2.0  right_only
3     2      NaN        2.0  right_only

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame df1df2 进行外连接,并设置 indicator=True。连接后的结果中添加了一个名为 _merge 的列,用于指示连接的来源。

indicator 参数也可以设置为字符串,此时该字符串将作为指示列的名称。

pd.merge(df1, df2, on="col1", how="outer", indicator="indicator_column")
Out[76]: 
   col1 col_left  col_right indicator_column
0     0        a        NaN        left_only
1     1        b        2.0             both
2     2      NaN        2.0       right_only
3     2      NaN        2.0       right_only

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame df1df2 进行外连接,并设置 indicator="indicator_column"。连接后的结果中添加了一个名为 indicator_column 的列,用于指示连接的来源。

merge() 函数的 suffixes 参数接受一个字符串列表或元组,用于在输入 DataFrame 的重叠列名后添加后缀,以区分结果列。

left = pd.DataFrame({"A": [1, 2], "B": [2, 2]})

right = pd.DataFrame({"A": [4, 5, 6], "B": [2, 2, 2]})

result = pd.merge(left, right, on="B")

result
Out[80]: 
   A_x  B  A_y
0    1  2    4
1    1  2    5

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行连接,并设置连接键为 B。由于两个 DataFrame 中都存在列名为 A 的列,所以连接后的结果中会出现重叠列名。为了区分这些列,我们可以使用 suffixes=("_l", "_r") 参数,将左侧 DataFrame 的列名后缀设置为 _l,将右侧 DataFrame 的列名后缀设置为 _r

result = pd.merge(left, right, on="B", suffixes=("_l", "_r"))

result
Out[82]: 
   A_l  B  A_r
0    1  2    4
1    1  2    5

在这个例子中,我们使用了 pd.merge() 函数将两个 DataFrame leftright 进行连接,并设置连接键为 B,同时设置了 suffixes=("_l", "_r") 参数。连接后的结果中的列名为 A_lBA_r,用于区分两个 DataFrame 中的重叠列。

DataFrame.join() 函数将多个可能具有不同索引的 DataFrame 的列组合成一个结果 DataFrame。

left = pd.DataFrame(
    {"A": ["A0", "A1", "A2"], "B": ["B0", "B1", "B2"]}, index=["K0", "K1", "K2"]
)


right = pd.DataFrame(
    {"C": ["C0", "C2", "C3"], "D": ["D0", "D2", "D3"]}, index=["K0", "K2", "K3"]
)


result = left.join(right)

result
Out[86]: 
     A   B    C    D
K0  A0  B0   C0   D0
K1  A1  B1  NaN  NaN
K2  A2  B2   C2   D2

在这个例子中,我们使用了 DataFrame.join() 函数将 DataFrame leftright 进行连接。连接的结果存储在变量 result 中。连接的方式是根据索引进行连接,即将 left DataFrame 和 right DataFrame 中具有相同索引的行进行连接。

result = left.join(right, how="outer")

result
Out[88]: 
      A    B    C    D
K0   A0   B0   C0   D0
K1   A1   B1  NaN  NaN
K2   A2   B2   C2   D2
K3  NaN  NaN   C3   D3

在这个例子中,我们使用了 DataFrame.join() 函数将 DataFrame leftright 进行外连接。外连接是指保留两个 DataFrame 中所有的行,如果某个行在其中一个 DataFrame 中不存在,则用 NaN 填充。连接后的结果存储在变量 result 中。

result = left.join(right, how="inner")

result
Out[90]: 
     A   B   C   D
K0  A0  B0  C0  D0
K2  A2  B2  C2  D2

在这个例子中,我们使用了 DataFrame.join() 函数将 DataFrame leftright 进行内连接。内连接是指只保留两个 DataFrame 中共有的行,其他的行将被丢弃。连接后的结果存储在变量 result 中。

[`DataFrame.join()`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.join.html#pandas.DataFrame.join) 方法可以接受一个可选的 `on` 参数,该参数可以是一个列名或多个列名,用于对齐传入的 [`DataFrame`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html#pandas.DataFrame)。

```python
left = pd.DataFrame(
    {
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
        "key": ["K0", "K1", "K0", "K1"],
    }
)


right = pd.DataFrame({"C": ["C0", "C1"], "D": ["D0", "D1"]}, index=["K0", "K1"])

result = left.join(right, on="key")

result
Out[94]: 
    A   B key   C   D
0  A0  B0  K0  C0  D0
1  A1  B1  K1  C1  D1
2  A2  B2  K0  C0  D0
3  A3  B3  K1  C1  D1

DataFrame.join() 方法可以接受一个可选的 on 参数,该参数可以是一个列名或多个列名,用于对齐传入的 DataFrame

result = pd.merge(
    left, right, left_on="key", right_index=True, how="left", sort=False
)


result
Out[96]: 
    A   B key   C   D
0  A0  B0  K0  C0  D0
1  A1  B1  K1  C1  D1
2  A2  B2  K0  C0  D0
3  A3  B3  K1  C1  D1

要使用多个键进行连接,传入的 DataFrame 必须具有 MultiIndex

left = pd.DataFrame(
    {
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
        "key1": ["K0", "K0", "K1", "K2"],
        "key2": ["K0", "K1", "K0", "K1"],
    }
)


index = pd.MultiIndex.from_tuples(
    [("K0", "K0"), ("K1", "K0"), ("K2", "K0"), ("K2", "K1")]
)


right = pd.DataFrame(
    {"C": ["C0", "C1", "C2", "C3"], "D": ["D0", "D1", "D2", "D3"]}, index=index
)


result = left.join(right, on=["key1", "key2"])

result
Out[101]: 
    A   B key1 key2    C    D
0  A0  B0   K0   K0   C0   D0
1  A1  B1   K0   K1  NaN  NaN
2  A2  B2   K1   K0   C1   D1
3  A3  B3   K2   K1   C3   D3

DataFrame.join() 方法的默认行为是执行左连接(left join),只使用调用 DataFrame 中找到的键。可以使用 how 参数指定其他连接类型。

result = left.join(right, on=["key1", "key2"], how="inner")

result
Out[103]: 
    A   B key1 key2   C   D
0  A0  B0   K0   K0  C0  D0
2  A2  B2   K1   K0  C1  D1
3  A3  B3   K2   K1  C3  D3

可以将一个具有 IndexDataFrame 与具有 MultiIndexDataFrame 在一个级别上进行连接。IndexnameMultiIndex 的级别名称匹配。

left = pd.DataFrame(
    {"A": ["A0", "A1", "A2"], "B": ["B0", "B1", "B2"]},
    index=pd.Index(["K0", "K1", "K2"], name="key"),
)


index = pd.MultiIndex.from_tuples(
    [("K0", "Y0"), ("K1", "Y1"), ("K2", "Y2"), ("K2", "Y3")],
    names=["key", "Y"],
)


right = pd.DataFrame(
    {"C": ["C0", "C1", "C2", "C3"], "D": ["D0", "D1", "D2", "D3"]},
    index=index,
)


result = left.join(right, how="inner")

result
Out[108]: 
         A   B   C   D
key Y                 
K0  Y0  A0  B0  C0  D0
K1  Y1  A1  B1  C1  D1
K2  Y2  A2  B2  C2  D2
    Y3  A2  B2  C3  D3

可以将两个 MultiIndex 进行连接,输入参数的 MultiIndex 必须完全用于连接,并且是左侧参数中索引的子集。

leftindex = pd.MultiIndex.from_product(
    [list("abc"), list("xy"), [1, 2]], names=["abc", "xy", "num"]
)


left = pd.DataFrame({"v1": range(12)}, index=leftindex)

left
Out[111]: 
            v1
abc xy num    
a   x  1     0
       2     1
    y  1     2
       2     3
b   x  1     4
       2     5
    y  1     6
       2     7
c   x  1     8
       2     9
    y  1    10
       2    11

rightindex = pd.MultiIndex.from_product(
    [list("abc"), list("xy")], names=["abc", "xy"]
)


right = pd.DataFrame({"v2": [100 * i for i in range(1, 7)]}, index=rightindex)

right
Out[114]: 
         v2
abc xy     
a   x   100
    y   200
b   x   300
    y   400
c   x   500
    y   600

left.join(right, on=["abc", "xy"], how="inner")
Out[115]: 
            v1   v2
abc xy num         
a   x  1     0  100
       2     1  100
    y  1     2  200
       2     3  200
b   x  1     4  300
       2     5  300
    y  1     6  400
       2     7  400
c   x  1     8  500
       2     9  500
    y  1    10  600
       2    11  600

DataFrame.join() 方法还可以将一个列表或元组的 DataFrame 一起传递给 join(),在它们的索引上进行连接。

right2 = pd.DataFrame({"v": [7, 8, 9]}, index=["K1", "K1", "K2"])

result = left.join([right, right2])

DataFrame.combine_first() 方法可以使用另一个 DataFrame 中的非缺失值更新一个 DataFrame 中的缺失值。

df1 = pd.DataFrame(
    [[np.nan, 3.0, 5.0], [-4.6, np.nan, np.nan], [np.nan, 7.0, np.nan]]
)


df2 = pd.DataFrame([[-42.6, np.nan, -8.2], [-5.0, 1.6, 4]], index=[1, 2])

result = df1.combine_first(df2)

result
Out[133]: 
     0    1    2
0  NaN  3.0  5.0
1 -4.6  NaN -8.2
2 -5.0  7.0  4.0

DataFrame.combine_first() 方法可以使用另一个 DataFrame 中的非缺失值更新一个 DataFrame 中的缺失值。

merge_ordered()

merge_ordered() 函数可以将有序数据(如数字或时间序列数据)进行合并,并可选择使用 fill_method 来填充缺失数据。

left = pd.DataFrame(
    {"k": ["K0", "K1", "K1", "K2"], "lv": [1, 2, 3, 4], "s": ["a", "b", "c", "d"]}
)


right = pd.DataFrame({"k": ["K1", "K2", "K4"], "rv": [1, 2, 3]})

pd.merge_ordered(left, right, fill_method="ffill", left_by="s")
Out[136]: 
     k   lv  s   rv
0   K0  1.0  a  NaN
1   K1  1.0  a  1.0
2   K2  1.0  a  2.0
3   K4  1.0  a  3.0
4   K1  2.0  b  1.0
5   K2  2.0  b  2.0
6   K4  2.0  b  3.0
7   K1  3.0  c  1.0
8   K2  3.0  c  2.0
9   K4  3.0  c  3.0
10  K1  NaN  d  1.0
11  K2  4.0  d  2.0
12  K4  4.0  d  3.0

merge_asof()

merge_asof() 函数类似于有序的左连接,但匹配是基于最近的键而不是相等的键。对于 left DataFrame 中的每一行,选择 right DataFrame 中最后一个键小于左键的行。两个 DataFrame 必须按键排序。

可选地,merge_asof() 可以通过在 by 键上进行匹配来执行分组合并。

trades = pd.DataFrame(
    {
        "time": pd.to_datetime(
            [
                "20160525 13:30:00.023",
                "20160525 13:30:00.038",
                "20160525 13:30:00.048",
                "20160525 13:30:00.048",
                "20160525 13:30:00.048",
            ]
        ),
        "ticker": ["MSFT", "MSFT", "GOOG", "GOOG", "AAPL"],
        "price": [51.95, 51.95, 720.77, 720.92, 98.00],
        "quantity": [75, 155, 100, 100, 100],
    },
    columns=["time", "ticker", "price", "quantity"],
)


quotes = pd.DataFrame(
    {
        "time": pd.to_datetime(
            [
                "20160525 13:30:00.023",
                "20160525 13:30:00.023",
                "20160525 13:30:00.030",
                "20160525 13:30:00.041",
                "20160525 13:30:00.048",
                "20160525 13:30:00.049",
                "20160525 13:30:00.072",
                "20160525 13:30:00.075",
            ]
        ),
        "ticker": ["GOOG", "MSFT", "MSFT", "MSFT", "GOOG", "AAPL", "GOOG", "MSFT"],
        "bid": [720.50, 51.95, 51.97, 51.99, 720.50, 97.99, 720.50, 52.01],
        "ask": [720.93, 51.96, 51.98, 52.00, 720.93, 98.01, 720.88, 52.03],
    },
    columns=["time", "ticker", "bid", "ask"],
)


trades
Out[139]: 
                     time ticker   price  quantity
0 2016-05-25 13:30:00.023   MSFT   51.95        75
1 2016-05-25 13:30:00.038   MSFT   51.95       155
2 2016-05-25 13:30:00.048   GOOG  720.77       100
3 2016-05-25 13:30:00.048   GOOG  720.92       100
4 2016-05-25 13:30:00.048   AAPL   98.00       100

quotes
Out[140]: 
                     time ticker     bid     ask
0 2016-05-25 13:30:00.023   GOOG  720.50  720.93
1 2016-05-25 13:30:00.023   MSFT   51.95   51.96
2 2016-05-25 13:30:00.030   MSFT   51.97   51.98
3 2016-05-25 13:30:00.041   MSFT   51.99   52.00
4 2016-05-25 13:30:00.048   GOOG  720.50  720.93
5 2016-05-25 13:30:00.049   AAPL   97.99   98.01
6 2016-05-25 13:30:00.072   GOOG  720.50  720.88
7 2016-05-25 13:30:00.075   MSFT   52.01   52.03

pd.merge_asof(trades, quotes, on="time", by="ticker")
Out[141]: 
                     time ticker   price  quantity     bid     ask
0 2016-05-25 13:30:00.023   MSFT   51.95        75   51.95   51.96
1 2016-05-25 13:30:00.038   MSFT   51.95       155   51.97   51.98
2 2016-05-25 13:30:00.048   GOOG  720.77       100  720.50  720.93
3 2016-05-25 13:30:00.048   GOOG  720.92       100  720.50  720.93
4 2016-05-25 13:30:00.048   AAPL   98.00       100     NaN     NaN

merge_asof() 在报价时间和交易时间之间的时间差小于 2ms

pd.merge_asof(trades, quotes, on="time", by="ticker", tolerance=pd.Timedelta("2ms"))
Out[142]: 
                     time ticker   price  quantity     bid     ask
0 2016-05-25 13:30:00.023   MSFT   51.95        75   51.95   51.96
1 2016-05-25 13:30:00.038   MSFT   51.95       155     NaN     NaN
2 2016-05-25 13:30:00.048   GOOG  720.77       100  720.50  720.93
3 2016-05-25 13:30:00.048   GOOG  720.92       100  720.50  720.93
4 2016-05-25 13:30:00.048   AAPL   98.00       100     NaN     NaN

merge_asof() 在报价时间和交易时间之间的时间差小于 10ms,并排除时间上的精确匹配。请注意,尽管我们排除了精确匹配(报价),但之前的报价仍然会传播到该时间点。

pd.merge_asof(
    trades,
    quotes,
    on="time",
    by="ticker",
    tolerance=pd.Timedelta("10ms"),
    allow_exact_matches=False,
)

Out[143]: 
                     time ticker   price  quantity    bid    ask
0 2016-05-25 13:30:00.023   MSFT   51.95        75    NaN    NaN
1 2016-05-25 13:30:00.038   MSFT   51.95       155  51.97  51.98
2 2016-05-25 13:30:00.048   GOOG  720.77       100    NaN    NaN
3 2016-05-25 13:30:00.048   GOOG  720.92       100    NaN    NaN
4 2016-05-25 13:30:00.048   AAPL   98.00       100    NaN    NaN

compare()

Series.compare()DataFrame.compare() 方法允许您比较两个 DataFrameSeries,并总结它们的差异。

df = pd.DataFrame(
    {
        "col1": ["a", "a", "b", "b", "a"],
        "col2": [1.0, 2.0, 3.0, np.nan, 5.0],
        "col3": [1.0, 2.0, 3.0, 4.0, 5.0],
    },
    columns=["col1", "col2", "col3"],
)


df
Out[145]: 
  col1  col2  col3
0    a   1.0   1.0
1    a   2.0   2.0
2    b   3.0   3.0
3    b   NaN   4.0
4    a   5.0   5.0

df2 = df.copy()

df2.loc[0, "col1"] = "c"

df2.loc[2, "col3"] = 4.0

df2
Out[149]: 
  col1  col2  col3
0    c   1.0   1.0
1    a   2.0   2.0
2    b   3.0   4.0
3    b   NaN   4.0
4    a   5.0   5.0

df.compare(df2)
Out[150]: 
  col1       col3      
  self other self other
0    a     c  NaN   NaN
2  NaN   NaN  3.0   4.0

默认情况下,如果两个对应的值相等,它们将显示为 NaN。此外,如果整行/列中的所有值都相等,则该行/列将从结果中省略。剩下的差异将对齐在列上。

将差异堆叠在行上。

df.compare(df2, align_axis=0)
Out[151]: 
        col1  col3
0 self     a   NaN
  other    c   NaN
2 self   NaN   3.0
  other  NaN   4.0

保留所有原始行和列,使用 keep_shape=True

df.compare(df2, keep_shape=True)
Out[152]: 
  col1       col2       col3      
  self other self other self other
0    a     c  NaN   NaN  NaN   NaN
1  NaN   NaN  NaN   NaN  NaN   NaN
2  NaN   NaN  NaN   NaN  3.0   4.0
3  NaN   NaN  NaN   NaN  NaN   NaN
4  NaN   NaN  NaN   NaN  NaN   NaN

保留所有原始值,即使它们相等。

df.compare(df2, keep_shape=True, keep_equal=True)
Out[153]: 
  col1       col2       col3      
  self other self other self other
0    a     c  1.0   1.0  1.0   1.0
1    a     a  2.0   2.0  2.0   2.0
2    b     b  3.0   3.0  3.0   4.0
3    b     b  NaN   NaN  4.0   4.0
4    a     a  5.0   5.0  5.0   5.0

Pandas 2 使用指南导读

Pandas 2 使用指南:1、十分钟入门Pandas

Pandas 2 使用指南:2、数据结构简介

Pandas 2 使用指南:3、基本功能

Pandas 2 使用指南:4、IO工具(文本、CSV、HDF5等)

Pandas 2 使用指南:5、PyArrow 功能介绍

Pandas 2 使用指南: 6、索引和选择数据

Pandas 2 使用指南:7、多级索引 / 高级索引

Pandas 2 使用指南:8、写时复制(Copy-on-Write,CoW)

Pandas 2 使用指南:9、合并、连接、串联和比较

Pandas 2 使用指南:10、重塑和透视表ReShapingand Pivot Tables

Pandas 2 使用指南:11、处理文本数据 Working with text data

Pandas 2 使用指南:12、处理缺失数据Working with missing data

Pandas 2 使用指南: 13、重复标签 Duplicate Labels

Pandas 2 使用指南:14、分类数据 Categorical data
Pandas 2 使用指南:15、可空整数数据类型、可空布尔数据类型

Pandas 2 使用指南:16、图表可视化

Pandas 2 使用指南:17、表格可视化

Pandas 2 使用指南:18、Groupby:拆分-应用-合并 split-apply-combine

Pandas 2 使用指南:19、窗口操作 Windowing operations

Pandas 2 使用指南:20、时间序列/日期功能
Pandas 2 使用指南:21、时间差 Timedelta

Pandas 2 使用指南:22、选项和设置

Pandas 2 使用指南:23、提升性能 Enhancing performance

Pandas 2 使用指南:24、大规模数据集的扩展

Pandas 2 使用指南:25、稀疏数据结构 Sparse data structures

Pandas 2 使用指南:26、常见问题解答 (FAQ)

Pandas 2 使用指南:27、Cookbook

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值