如何将pandas DataFrame的列拆分为多个变量

15

如果维度匹配,列表或numpy数组可以解包到多个变量中。 对于3xN数组,以下内容可行:

import numpy as np 
a,b =          [[1,2,3],[4,5,6]]
a,b = np.array([[1,2,3],[4,5,6]])
# result: a=[1,2,3],   b=[4,5,6]

如何使pandas DataFrame 的列具有类似的行为? 扩展上面的示例:

import pandas as pd 
df = pd.DataFrame([[1,2,3],[4,5,6]])
df.columns = ['A','B','C']    # Rename cols and
df.index = ['i', 'ii']        # rows for clarity

以下内容未按预期工作:

a,b = df.T
# result: a='i',   b='ii'
a,b,c = df
# result: a='A',   b='B',   c='C'

然而,我想要的是以下内容:

a,b,c = unpack(df)
result: a=df['A'], b=df['B'], c=df['C']

在Pandas中是否已经有unpack函数可用?或者是否可以以一种简单的方式模仿它?


并不是因为我不想麻烦于列名,才这么说。通过解包,我所指的就是PEP3132中所描述的东西。 - normanius
1
当然可以,但我不明白为什么你想要解压它们而不是直接访问数据框对象本身的名称...除了另一个可能会使您的DF中的列不必要地保持活动状态的引用之外,a = df.a对您有什么好处? - Jon Clements
可读性,例如,如果列名过于复杂。 - normanius
1
如果您真的想要,您可以直接访问df.values,这将为您提供底层的numpy数组... - Jon Clements
1
没事的... :) - Jon Clements
显示剩余5条评论
2个回答

28

我刚刚发现以下代码可以实现我想要的功能,已经接近了我的目标:

a,b,c = df.T.values        # Common
a,b,c = df.T.to_numpy()    # Recommended
# a,b,c = df.T.as_matrix() # Deprecated

细节:事情总是比人们想象的要复杂一些。请注意,pd.DataFrame 列分别存储在 Series 中。调用 df.values(或更好的方式:df.to_numpy())可能会非常昂贵,因为它将列组合在一个单独的 ndarray 中,这可能涉及复制操作和类型转换。此外,生成的容器具有能够容纳数据框中所有数据的单个 dtype
总之,上述方法丢失了每列的 dtype 信息,并且可能很昂贵。从技术上讲,按以下一种方式之一迭代列是更加清晰的做法(还有更多选项):
# The following alternatives create VIEWS!
a,b,c = (v for _,v in df.items())      # returns pd.Series
a,b,c = (df[c] for c in df)            # returns pd.Series

注意上面创建了views!修改数据可能会触发SettingWithCopyWarning警告。
a.iloc[0] = "blabla"    # raises SettingWithCopyWarning

如果您想修改解包后的变量,您需要复制这些列。

# The following alternatives create COPIES!
a,b,c = (v.copy() for _,v in df.items())      # returns pd.Series
a,b,c = (df[c].copy() for c in df)            # returns pd.Series
a,b,c = (df[c].to_numpy() for c in df)        # returns np.ndarray

虽然这种写法看起来更整洁,但需要更多的字符。我个人不建议在生产代码中使用上述方法。不过为了避免在交互式命令行会话中键入(例如),这仍然是一个合理的选项{{...}}。

# More verbose and explicit alternatives
a,b,c = df["the first col"], df["the second col"], df["the third col"]
a,b,c = df.iloc[:,0], df.iloc[:,1], df.iloc[:,2]

1
从版本0.24.0开始,您还可以执行以下操作: a, b, c = df.T.to_numpy() - Ben Vincent
2
小心处理。转置可能会改变数据的类型,因为类型是特定于列的。如果您有一些整数,它们可能会被转换为对象或浮点数,在后一种情况下,您将失去精度。 - Emsi
@Emsi 感谢您的评论。我已经更新了我的答案,并添加了一些细节。请注意,有关列类型的信息并不是因为转置而丢失的,而是因为 to_numpy - normanius

0

dataframe.values 方法确实是一个不错的解决方案,但它涉及构建一个 numpy 数组。

如果您想在拆包后访问 pandas series 方法,我个人使用不同的方法。

对于像我这样经常使用链式方法的人,我通过向 pandas 添加自定义拆包方法来解决。请注意,这可能不适用于生产管道,但在临时数据分析中非常方便。

df = pd.DataFrame({
    "lat": [30, 40], 
    "lon": [0, 1],
})

这种方法涉及在.unpack()调用上返回一个生成器。

from typing import Tuple

def unpack(self: pd.DataFrame) -> Tuple[pd.Series]:
    return (
        self[col]
        for col in self.columns
    )

pd.DataFrame.unpack = unpack

这可以有两种主要用途。

要么直接作为解决方案:

lat, lon = df.unpack()

或者,可以在方法链接中使用。 想象一个地理函数,它必须在第一个参数中使用纬度系列,在第二个参数中使用经度,命名为do_something_geographical(lat, lon)

df_result = (
    df
        .(...some method chaining...)
        .assign(
            geographic_result=lambda dataframe: do_something_geographical(dataframe[["lat", "lon"]].unpack())
        )
        .(...some method chaining...)
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接