在 Pandas 数据框上链接方法时,列引用语法似乎不一致

joe*_*lom 5 python r pandas dplyr

我有点困惑,为什么在 Pandas 数据框中引用列的语法会根据调用的方法而有所不同。采用以下示例方法链

import pandas as pd

iris = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
iris.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']
(iris
    .loc[:, ['SepalLength', 'PetalWidth', 'Species']]
    .where(iris['SepalLength'] > 4.6)
    .assign(PetalWidthx2 = lambda x_iris: x_iris['PetalWidth'] * 2)
    .groupby('Species')
    .agg({'SepalLength': 'mean', 'PetalWidthx2': 'std'}))
Run Code Online (Sandbox Code Playgroud)

这里,有三种不同的语法用于引用 iris 数据框中的列:

  • loc, groupby, 和agg所有人都知道字符串指的是数据框中的一列。
  • where 需要显式引用数据框。
  • 在assign方法中显式引用数据帧将导致对原始 iris 数据帧执行操作,而不是已通过调用loc和修改的副本where。这里,lambda需要参考修改后的数据帧副本的当前状态。
  • 除了上述之外,还有query,它将整个方法输入作为字符串:iris.query('SepalLength > 4.6'),但这里的熊猫文档明确指出这是针对特殊用例的:

    query() 的一个用例是当您有一组 DataFrame 对象时,这些对象具有共同的列名(或索引级别/名称)的子集。您可以将相同的查询传递给两个框架,而无需指定您对查询感兴趣的框架

为了举例说明我所说的一致数据框列引用语法的含义,可以与 R-package 进行比较dplyr,其中数据框中的列对于所有管道函数调用都使用相同的语法进行引用。

library(dplyr)

# The iris data set is preloaded in R
colnames(iris) = c('SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species')
iris %>% 
    select(SepalLength, PetalWidth, Species) %>% 
    filter(SepalLength > 4.6) %>%  
    mutate(PetalWidth2x = PetalWidth * 2) %>% 
    group_by(Species) %>% 
    summarise(SepalLength = mean(SepalLength), PetalWidth2x = sd(PetalWidth2x))
Run Code Online (Sandbox Code Playgroud)

使用这些不同的方式来引用数据框列,而不是将loc,groupby和agg所有方法使用的简单语法应用到所有方法中,pandas 是否有优势(如果是,这些好处是什么)?或者这更像是在assign和where方法中使用字符串作为数据框列名的一些潜在问题的解决方法?

Ste*_*uch 5

引用Marius 的评论:

我认为 pandas 和 dplyr 之间最大的区别在于,pandas 在 Python 现有的语法规则中工作,这些规则对于未加引号的符号可以表示的内容(基本上是当前范围内的对象)非常严格......

我相信这是正确的,所以让我们稍微扩展一下。


loc, groupby, 和agg所有人都知道字符串指的是数据框中的一列。

.loc[:, ['SepalLength', 'PetalWidth', 'Species']]
.groupby('Species')
.agg({'SepalLength': 'mean', 'PetalWidthx2': 'std'}))
Run Code Online (Sandbox Code Playgroud)

在所有三种情况下,字符串都是该上下文中的有效实体。也就是说,字符串本身就提供了足够的信息来执行操作。不像...


where 需要显式引用数据框。

.where(iris['SepalLength'] > 4.6)
Run Code Online (Sandbox Code Playgroud)

在 的情况下where,Python 要求运算符>对某些内容进行操作。通过选择特定的数据框列,该列引用了一个对象,并且__gt__将针对该对象调用该方法。

相反,如果我们希望语法看起来像:

.where('SepalLength' > 4.6)
Run Code Online (Sandbox Code Playgroud)

我们需要某种方式来告诉 Python>运算符在这种情况下的含义。评估发生在传递给 之前where。现有的语言特性是提供我们自己的对象,并定义了适当的方法,这就是 Pandas 设计者所做的。>字符串的默认操作只是在此上下文中没有用。


在assign方法中显式引用数据帧将导致对原始 iris 数据帧执行操作,而不是已通过调用loc和修改的副本where。这里,lambda需要参考修改后的数据帧副本的当前状态。

.assign(PetalWidthx2 = lambda x_iris: x_iris['PetalWidth'] * 2)
Run Code Online (Sandbox Code Playgroud)

如果.assign将被用作数据框的第一种方法,在进行任何过滤之前,我们可以简单地将其写为

.assign(PetalWidthx2 = iris['PetalWidth'] * 2)
Run Code Online (Sandbox Code Playgroud)

因为变量iris已经存在并且与我们要操作的数据帧相同。

但是,由于之前调用.loc并.where更改了我们希望调用的数据帧.assign,因此它不再与iris数据帧相同,并且没有定义的变量引用修改后的数据帧。由于 Pandas 使用 Python 现有的语法规则,因此它可以利用lambda,在这种情况下,它本质上允许对 进行操作self:.assign被调用对象的当前状态。文档中有一个例子。

这使用了方法的 **kwargs,它允许指定任意数量的参数(新列名)及其参数(新列的值)。**kwargsparameter=argument对在内部被解释为字典key:value对,如源代码所示。


除了上述之外,还有query,它将整个方法输入作为一个字符串:iris.query('SepalLength > 4.6'),但这里的pandas文档明确指出这是针对特殊用例的

在 的情况下query,传递的字符串是一个表达式,将由后端编译和执行,通常比执行 python 代码快得多。这是一种特殊情况,因为可用操作非常有限,并且后端引擎的设置时间很大,因此它实际上只对相当大的数据集有用。