Python:熊猫系列 - 为什么要使用loc?

Run*_*ean 47 python series loc pandas

为什么我们将'loc'用于pandas数据帧?似乎以下代码使用或不使用loc编译anr以相同的速度运行

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

100 loops, best of 3: 11.9 ms per loop
Run Code Online (Sandbox Code Playgroud)

要么

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

100 loops, best of 3: 12 ms per loop
Run Code Online (Sandbox Code Playgroud)

那么为什么要使用loc?

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

你可以只使用数据框的getitem进行列检索 :

*

df['time']    # equivalent to df.loc[:, 'time']
Run Code Online (Sandbox Code Playgroud)

它没有说明为什么我们使用loc,虽然它确实解释了loc的许多功能,但我的具体问题是"为什么不完全省略loc"?我已经接受了以下非常详细的答案.

还有其他帖子的答案(我认为不是答案)在讨论中非常隐藏,任何寻找我正在寻找的人都会发现很难找到信息,而且提供的答案会更好我的问题.

unu*_*tbu 51

  • 显式优于隐式.

    df[boolean_mask]选择其中boolean_mask为True的行,但是当您可能不希望它时df有一个极端情况:when 具有布尔值的列标签:

    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
    
    Run Code Online (Sandbox Code Playgroud)

    您可能希望用于df[[True]]选择True列.相反,它提出了一个ValueError:

    In [230]: df[[True]]
    ValueError: Item wrong length 1 instead of 3.
    
    Run Code Online (Sandbox Code Playgroud)

    使用范围loc:

    In [231]: df.loc[[True]]
    Out[231]: 
       False  True 
    0      3      1
    
    Run Code Online (Sandbox Code Playgroud)

    相反,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
    
    Run Code Online (Sandbox Code Playgroud)

    因此,df[boolean_mask]并不总是表现得相同df.loc[boolean_mask].即使这可能是一个不太可能的用例,我建议总是使用df.loc[boolean_mask]而不是df[boolean_mask]因为df.loc语法的含义是明确的.随df.loc[indexer]你自动知道df.loc选择行.相反,目前尚不清楚是否df[indexer]会在ValueError不知道有关indexer和的详细信息的情况下选择行或列(或加注)df.

  • 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
    
    Run Code Online (Sandbox Code Playgroud)
  • 切片传递到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
    
    Run Code Online (Sandbox Code Playgroud)

  • 我想说最后一点是最重要的,并将其推到顶部,这实际上是危险的不同行为 (6认同)
  • 您为什么不在列名周围使用引号?df [['True']]`不能正常工作吗? (4认同)
  • 与切片的一个重要区别是 loc[1:2] 适用于索引,而 df2[1:2] 适用于简单的行顺序,无论索引如何。也就是说,在上面的最后一个示例中,如果索引以 [3,4] 开头,则 loc[1:2] 将不会返回任何内容,而 df2[1:2] 仍将返回索引为 3 的第一行。 (4认同)
  • @LS `df[['True']]` 在示例中似乎无法正常工作。列名不是字符串,它们是布尔对象。看起来 pandas 不需要列名称是字符串(与例如 R 不同,其中名称(df)是字符并且 [[]] 强制输入为字符) (2认同)

Esf*_*iar 6

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

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

这:

mask = df["salary"] > 60000
df[mask]["upper management"] = True
Run Code Online (Sandbox Code Playgroud)

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

但这成功了:

mask = df["salary"] > 60000
df.loc[mask,"upper management"] = True
Run Code Online (Sandbox Code Playgroud)

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


Sea*_*ean 5

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

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

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

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

df[df['mark'] >= 50]['text_rating'] = 'Pass'
Run Code Online (Sandbox Code Playgroud)

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

2个选项可用:

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

第二种情况例如:

df['text_rating'][df['mark'] >= 50] = 'Pass'
Run Code Online (Sandbox Code Playgroud)

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

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

df.loc[df['mark'] >= 50, 'text_rating'] = 'Pass'
Run Code Online (Sandbox Code Playgroud)

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

不使用 .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)
Run Code Online (Sandbox Code Playgroud)

使用 .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)
Run Code Online (Sandbox Code Playgroud)

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

关于“链式赋值”的更详细解释,可以参考另一篇相关文章如何处理pandas中的SettingWithCopyWarning?尤其是 cs95 的答案。这篇文章很好地解释了使用 .loc 的功能差异。我这里只是补充一下系统性能(执行时间)的差异。


归档时间:

查看次数:

15033 次

最近记录:

7 年,4 月 前