pandas .at 与 .loc
- 2025-03-13 08:51:00
- admin 原创
- 53
问题描述:
我一直在探索如何优化我的代码并遇到了pandas
.at
方法。根据文档
快速基于标签的标量访问器
与 loc 类似,at 提供基于标签的标量查找。您也可以使用这些索引器进行设置。
因此我运行了一些样本:
设置
import pandas as pd
import numpy as np
from string import letters, lowercase, uppercase
lt = list(letters)
lc = list(lowercase)
uc = list(uppercase)
def gdf(rows, cols, seed=None):
"""rows and cols are what you'd pass
to pd.MultiIndex.from_product()"""
gmi = pd.MultiIndex.from_product
df = pd.DataFrame(index=gmi(rows), columns=gmi(cols))
np.random.seed(seed)
df.iloc[:, :] = np.random.rand(*df.shape)
return df
seed = [3, 1415]
df = gdf([lc, uc], [lc, uc], seed)
print df.head().T.head().T
df
看起来像:
a
A B C D E
a A 0.444939 0.407554 0.460148 0.465239 0.462691
B 0.032746 0.485650 0.503892 0.351520 0.061569
C 0.777350 0.047677 0.250667 0.602878 0.570528
D 0.927783 0.653868 0.381103 0.959544 0.033253
E 0.191985 0.304597 0.195106 0.370921 0.631576
让我们使用.at
和.loc
并确保我得到相同的东西
print "using .loc", df.loc[('a', 'A'), ('c', 'C')]
print "using .at ", df.at[('a', 'A'), ('c', 'C')]
using .loc 0.37374090276
using .at 0.37374090276
使用测试速度.loc
%%timeit
df.loc[('a', 'A'), ('c', 'C')]
10000 loops, best of 3: 180 µs per loop
使用测试速度.at
%%timeit
df.at[('a', 'A'), ('c', 'C')]
The slowest run took 6.11 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 8 µs per loop
这看起来是一个巨大的速度提升。即使在缓存阶段也6.11 * 8
比180
问题
的局限性是什么.at
?我很想使用它。文档说它与 类似,.loc
但行为并不相似。示例:
# small df
sdf = gdf([lc[:2]], [uc[:2]], seed)
print sdf.loc[:, :]
A B
a 0.444939 0.407554
b 0.460148 0.465239
print sdf.at[:, :]
结果为TypeError: unhashable type
因此,即使意图是相似,但显然也是不一样的。
话虽如此,谁能提供指导,说明该.at
方法可以做什么,不可以做什么?
解决方案 1:
更新:df.get_value
自 0.21.0 版起已弃用。今后建议使用df.at
或。df.iat
df.at
一次只能访问一个值。
df.loc
可以选择多行和/或多列。
请注意,还有df.get_value
,它可能在访问单个值时更快:
In [25]: %timeit df.loc[('a', 'A'), ('c', 'C')]
10000 loops, best of 3: 187 µs per loop
In [26]: %timeit df.at[('a', 'A'), ('c', 'C')]
100000 loops, best of 3: 8.33 µs per loop
In [35]: %timeit df.get_value(('a', 'A'), ('c', 'C'))
100000 loops, best of 3: 3.62 µs per loop
在底层,df.at[...]
调用df.get_value
,但它也对键进行一些类型检查。
解决方案 2:
正如您询问的局限性.at
,这是我最近遇到的一件事(使用 pandas 0.22)。让我们使用文档中的示例:
df = pd.DataFrame([[0, 2, 3], [0, 4, 1], [10, 20, 30]], index=[4, 5, 6], columns=['A', 'B', 'C'])
df2 = df.copy()
A B C
4 0 2 3
5 0 4 1
6 10 20 30
如果我现在这么做
df.at[4, 'B'] = 100
结果正如预期的那样
A B C
4 0 100 3
5 0 4 1
6 10 20 30
然而,当我尝试做
df.at[4, 'C'] = 10.05
似乎.at
试图保存数据类型(此处int
:):
A B C
4 0 100 10
5 0 4 1
6 10 20 30
这似乎与以下内容有区别.loc
:
df2.loc[4, 'C'] = 10.05
得到所需的
A B C
4 0 2 10.05
5 0 4 1.00
6 10 20 30.00
上面例子中的危险之处在于它悄无声息地发生(从float
到 的转换int
)。当对字符串尝试相同操作时,它会抛出一个错误:
df.at[5, 'A'] = 'a_string'
ValueError: 十进制 int() 的无效文字:'a_string'
但是,如果使用一个实际有效的字符串,它就会起作用,int()
如@n1k31t4 在评论中指出的那样,例如
df.at[5, 'A'] = '123'
A B C
4 0 2 3
5 123 4 1
6 10 20 30
解决方案 3:
除上述内容外,Pandas文档中还at
指出:
访问行/列标签对的单个值。
与 loc 类似,两者都提供基于标签的查找。如果您只需要获取或设置 DataFrame 或 Series 中的单个值,请使用 at。
对于设置数据loc
和at
类似,例如:
df = pd.DataFrame({'A': [1,2,3], 'B': [11,22,33]}, index=[0,0,1])
和loc
将at
产生相同的结果
df.at[0, 'A'] = [101,102]
df.loc[0, 'A'] = [101,102]
A B
0 101 11
0 102 22
1 3 33
df.at[0, 'A'] = 103
df.loc[0, 'A'] = 103
A B
0 103 11
0 103 22
1 3 33
此外,对于访问单个值,两者是相同的
df.loc[1, 'A'] # returns a single value (<class 'numpy.int64'>)
df.at[1, 'A'] # returns a single value (<class 'numpy.int64'>)
3
但是,当匹配多个值时,loc
将返回 DataFrame 中的一组行/列,而at
将返回一个值数组
df.loc[0, 'A'] # returns a Series (<class 'pandas.core.series.Series'>)
0 103
0 103
Name: A, dtype: int64
df.at[0, 'A'] # returns array of values (<class 'numpy.ndarray'>)
array([103, 103])
更重要的是,loc
可以用于匹配一组行/列,并且可以只给出一个索引,而at
必须接收列
df.loc[0] # returns a DataFrame view (<class 'pandas.core.frame.DataFrame'>)
A B
0 103 11
0 103 22
# df.at[0] # ERROR: must receive column
解决方案 4:
.at
是一种与 相比优化的数据访问方法.loc
。
.loc
数据框选择由其参数中给出的 indexed_rows 和 labeled_columns 定位的所有元素。相反,.at
选择位于给定 indexed_row 和 labeled_columns 的数据框的特定元素。
此外,.at
以一行和一列作为输入参数,而.loc
可能采用多行和多列。输出使用.at
单个元素,.loc
可能使用 Series 或 DataFrame。
解决方案 5:
的另一个限制.at
是.loc
接受其他输入(以索引为交换),例如条件,而.at
不能:
> df = pd.DataFrame([[1, 2],
[0, 0],
],
columns=["A", "B"])
> df["A"] > 0 # is a pd.Series of bool values
> df.loc[df["A"] > 0, "B"] # provides the first line in this example
> df.at[df["A"] > 0, "B"] # using .at will raise InvalidIndexError
扫码咨询,免费领取项目管理大礼包!