100

为什么我们对 pandas 数据框使用“loc”?似乎以下代码无论是否使用 loc 都编译 anr 以类似的速度运行

%timeit df_user1 = df.loc[df.user_id=='5561']

100 loops, best of 3: 11.9 ms per loop

或者

%timeit df_user1_noloc = df[df.user_id=='5561']

100 loops, best of 3: 12 ms per loop

那么为什么要使用 loc 呢?

编辑:这已被标记为重复问题。但是虽然pandas iloc vs ix vs loc 的解释?确实提到*

您只需使用数据框的 getitem即可进行列检索:

*

df['time']    # equivalent to df.loc[:, 'time']

它没有说明我们为什么使用 loc,尽管它确实解释了 loc 的许多特性,但我的具体问题是“为什么不完全省略 loc”?我已经接受了下面一个非常详细的答案。

此外,其他帖子的答案(我认为不是答案)在讨论中非常隐藏,任何搜索我正在寻找的东西的人都会发现很难找到信息,并且提供的答案会更好地服务我的问题。

4

3 回答 3

90
  • 显式优于隐式。

    df[boolean_mask]选择boolean_mask为 True 的行,但在您可能不希望它的情况下存在极端情况:当df具有布尔值列标签时:

    In [229]: df = pd.DataFrame({True:[1,2,3],False:[3,4,5]}); df
    Out[229]: 
       False  True 
    0      3      1
    1      4      2
    2      5      3
    

    您可能希望使用df[[True]]来选择True列。相反,它提出了一个ValueError

    In [230]: df[[True]]
    ValueError: Item wrong length 1 instead of 3.
    

    与使用loc

    In [231]: df.loc[[True]]
    Out[231]: 
       False  True 
    0      3      1
    

    相反,ValueError即使 的结构与上述df2几乎相同,以下内容也不会出现df1

    In [258]: df2 = pd.DataFrame({'A':[1,2,3],'B':[3,4,5]}); df2
    Out[258]: 
       A  B
    0  1  3
    1  2  4
    2  3  5
    
    In [259]: df2[['B']]
    Out[259]: 
       B
    0  3
    1  4
    2  5
    

    因此,df[boolean_mask]并不总是表现得与 相同df.loc[boolean_mask]。尽管这可以说是一个不太可能的用例,但我建议始终使用df.loc[boolean_mask]而不是,df[boolean_mask]因为df.loc's 语法的含义是明确的。df.loc[indexer]你会自动知道这是df.loc选择行。相反,不清楚是否会在不知道and细节的情况下df[indexer]选择行或列(或 raise ) 。ValueErrorindexerdf

  • df.loc[row_indexer, column_index]可以选择行列。df[indexer]只能根据值的类型和列值的类型来选择行列(再次,它们是布尔值吗?)。indexerdf

    In [237]: df2.loc[[True,False,True], 'B']
    Out[237]: 
    0    3
    2    5
    Name: B, dtype: int64
    
  • 当切片被传递到df.loc端点时,该范围包括。当一个切片被传递给 时df[...],该切片被解释为一个半开区间:

    In [239]: df2.loc[1:2]
    Out[239]: 
       A  B
    1  2  4
    2  3  5
    
    In [271]: df2[1:2]
    Out[271]: 
       A  B
    1  2  4
    
于 2016-08-11T02:08:40.420 回答
8

使用和不使用 .loc 对多列“链式分配”的性能考虑

让我从系统性能的考虑来补充已经很好的答案。

问题本身包括对使用和不使用 .loc 的 2 段代码的系统性能(执行时间)的比较。引用的代码示例的执行时间大致相同。但是,对于其他一些代码示例,使用和不使用 .loc 的执行时间可能存在相当大的差异:例如数倍或更多!

pandas 数据框操作的一个常见情况是我们需要创建一个从现有列的值派生的新列。我们可以使用下面的代码来过滤条件(基于现有列)并为新列设置不同的值:

df[df['mark'] >= 50]['text_rating'] = 'Pass'

但是,这种“链式分配”不起作用,因为它可以创建“副本”而不是“视图”,并且基于此“副本”分配给新列不会更新原始数据框。

2 个可用选项:

    1. 我们可以使用 .loc,或者
    1. 不使用 .loc 的另一种方式编码

第二种情况例如:

df['text_rating'][df['mark'] >= 50] = 'Pass'

通过将过滤放在最后(在指定新列名之后),分配可以很好地与更新的原始数据框配合使用。

使用 .loc 的解决方案如下:

df.loc[df['mark'] >= 50, 'text_rating'] = 'Pass'

现在,让我们看看它们的执行时间:

不使用 .loc

%%timeit 
df['text_rating'][df['mark'] >= 50] = 'Pass'

2.01 ms ± 105 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

使用 .loc

%%timeit 
df.loc[df['mark'] >= 50, 'text_rating'] = 'Pass'

577 µs ± 5.13 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

正如我们所见,使用 .loc 后,执行时间快了 3 倍以上!

关于“链式赋值”更详细的解释,可以参考另一篇相关帖子How to deal with SettingWithCopyWarning in pandas? 特别是 cs95 的答案。这篇文章很好地解释了使用 .loc 的功能差异。我这里只是补充一下系统性能(执行时间)的差异。

于 2021-01-24T20:50:55.870 回答
3

除了已经说过的(在不使用 loc 的情况下将 True、False 作为列名的问题,以及使用 loc 选择行和列的能力以及对行和列选择进行切片的能力),另一个很大的区别是您可以使用loc 将值分配给特定的行和列。如果您尝试使用布尔系列选择数据框的子集并尝试更改该子集选择的值,您可能会收到 SettingWithCopy 警告。

假设您正在尝试更改工资大于 60000 的所有行的“上层管理”列。

这个:

mask = df["salary"] > 60000
df[mask]["upper management"] = True

抛出警告“试图在数据帧的切片副本上设置一个值”并且不起作用,因为 df[mask] 创建一个副本并尝试更新该副本的“上层管理”没有效果在原始df上。

但这成功了:

mask = df["salary"] > 60000
df.loc[mask,"upper management"] = True

请注意,在这两种情况下,您都可以执行df[df["salary"] > 60000]or df.loc[df["salary"] > 60000],但我认为首先将布尔条件存储在变量中更简洁。

于 2020-12-30T05:44:51.763 回答