在 Pandas/Python 中使用 loc 和仅使用方括号过滤列有什么区别?
- 2025-02-17 09:25:00
- admin 原创
- 59
问题描述:
我注意到在 Pandas DataFrame 中选择列的三种方法:
使用 loc 选择列的第一种方法:
df_new = df.loc[:, 'col1']
第二种方法-似乎更简单,更快捷:
df_new = df['col1']
第三种方法-最方便:
df_new = df.col1
这三种方法有区别吗?我认为没有区别,如果是这样的话,我宁愿使用第三种方法。
我最好奇的是为什么似乎有三种方法可以做同样的事情。
解决方案 1:
在以下情况下,它们的行为相同:
选择单列(与选择 A 列
df['A']
相同)df.loc[:, 'A']
选择列表(与选择 A、B 和 C 列
df[['A', 'B', 'C']]
相同)df.loc[:, ['A', 'B', 'C']]
按行切片(
df[1:3]
与 -> 选择第 1 行和第 2 行相同。df.iloc[1:3]
但请注意,如果使用loc
而不是 来切片行,则假设您有一个RangeIndexiloc
,您将获得第 1、2 和 3 行。请在此处查看详细信息。)
但是[]
在下列情况下不起作用:
您可以使用以下方式选择单行
df.loc[row_label]
您可以使用以下方式选择行列表
df.loc[[row_label1, row_label2]]
您可以使用以下方式对列进行切片
df.loc[:, 'A':'C']
这三项是无法用 来完成的[]
。更重要的是,如果你的选择涉及行和列,那么分配就会变得有问题。
df[1:3]['A'] = 5
这将选择行 1 和 2,然后选择返回对象的列“A”并为其分配值 5。问题是,返回的对象可能是副本,因此这可能不会更改实际的 DataFrame。这会引发 SettingWithCopyWarning。进行此分配的正确方法是:
df.loc[1:3, 'A'] = 5
使用.loc
,您一定可以修改原始 DataFrame。它还允许您对列进行切片 ( df.loc[:, 'C':'F']
)、选择单行 ( df.loc[5]
) 以及选择行列表 ( df.loc[[1, 2, 5]]
)。
还请注意,这两个不是同时包含在 API 中的。.loc
后来才作为更强大、更明确的索引器添加。有关更多详细信息,请参阅unutbu 的回答。
注意:使用[]
vs获取列.
是完全不同的主题。.
只是为了方便。它只允许访问名称为有效 Python 标识符的列(即它们不能包含空格,不能由数字组成……)。当名称与 Series/DataFrame 方法冲突时不能使用它。它也不能用于不存在的列(即df.a = 1
如果没有列,则赋值将不起作用a
)。除此之外,.
和[]
是相同的。
解决方案 2:
loc
当索引不是数字(例如 DatetimeIndex)时特别有用,因为您可以从索引中获取具有特定标签的行:
df.loc['2010-05-04 07:00:00']
df.loc['2010-1-1 0:00:00':'2010-12-31 23:59:59 ','Price']
但是[]
旨在获取具有特定名称的列:
df['Price']
您[]
还可以过滤行,但它更为复杂:
df[df['Date'] < datetime.datetime(2010,1,1,7,0,0)]['Price']
解决方案 3:
如果你不清楚哪种方法(至少)是适合你的用例的推荐方法,请查看pandas 教程中的简要说明:
选择数据子集时,
[]
使用方括号。在这些括号内,您可以使用单个列/行标签、列/行标签列表、标签片段、条件表达式或冒号。
loc
使用行和列名称选择特定行和/或列iloc
使用表格中的位置时选择特定的行和/或列您可以根据/为选择分配新值。
loc
`iloc`
我强调了一些要点,以使它们的用例差异更加清晰。
解决方案 4:
当您创建具有多列的数据框时,df.loc[] 和 df[] 之间似乎存在差异。
您可以参考这个问题:
有没有一种好的方法可以使用.loc 生成多列?
这里,您不能使用 生成多列df.loc[:,['name1','name2']]
,但只需使用双括号就可以了df[['name1','name2']]
。 (我想知道为什么它们的行为不同。)
解决方案 5:
分配方面有一个重要的区别。这两个分配并不等同:
df['j'] = pd.Series(...)
df.loc[:,'j'] = pd.Series(...)
索引用
[...]
替换系列。新系列带有自己的dtype
,现有的dtype
将被丢弃。用索引
loc[...]
替换值,但重用现有系列及其dtype
,可能会发生向上转换以适应新值。
int32
看看使用时如何忽略旧内容[...]
:
import pandas as pd
import numpy.random as npr
n = 4
# NumPy returns dtype int32
df = pd.DataFrame({
'j': npr.randint(1, 10, n),
'k': npr.randint(1, 10, n)})
print(df.dtypes)
# uint8 series
s = pd.Series(npr.randint(1, 10, n), dtype='uint8')
# Using [...]: uint8 series replaces uint32 series
df2 = df.copy()
df2['j'] = s
print(df2.dtypes)
# Using loc[...]: uint8 data upcasted to existing uint32
df3 = df.copy()
df3.loc[:,'j'] = s
print(df3.dtypes)
j int32 ⇠ original dtype
k int32
dtype: object
j uint8 ⇠ with [...]
k int32
dtype: object
j int32 ⇠ with loc[...]
k int32
dtype: object