Pandas .loc无KeyError

33
>>> pd.DataFrame([1], index=['1']).loc['2']  # KeyError
>>> pd.DataFrame([1], index=['1']).loc[['2']]  # KeyError
>>> pd.DataFrame([1], index=['1']).loc[['1','2']]  # Succeeds, as in the answer below. 

我希望有一些东西,在两个方面都不会失败。

>>> pd.DataFrame([1], index=['1']).loc['2']  # KeyError
>>> pd.DataFrame([1], index=['1']).loc[['2']]  # KeyError

是否有类似于loc的函数可以优雅地处理这个问题,或者其他表达此查询的方式?

7个回答

24

对@AlexLenail评论的更新
他说的没错,这对于大型列表来说会很慢。我做了更多的调查,并发现索引和列都可以使用intersection方法。我不确定算法复杂度,但实际上要快得多。

你可以像这样做。

good_keys = df.index.intersection(all_keys)
df.loc[good_keys]

就像你的例子一样

df = pd.DataFrame([1], index=['1'])
df.loc[df.index.intersection(['2'])]

以下是一个小实验

n = 100000

# Create random values and random string indexes
# have the bad indexes contain extra values not in DataFrame Index
rand_val = np.random.rand(n)
rand_idx = []
for x in range(n):
    rand_idx.append(str(x))

bad_idx = []
for x in range(n*2):
    bad_idx.append(str(x))

df = pd.DataFrame(rand_val, index=rand_idx)
df.head()

def get_valid_keys_list_comp():
    # Return filtered DataFrame using list comprehension to filter keys
    vkeys = [key for key in bad_idx if key in df.index.values]
    return df.loc[vkeys]

def get_valid_keys_intersection():
    # Return filtered DataFrame using list intersection() to filter keys
    vkeys = df.index.intersection(bad_idx)
    return df.loc[vkeys]

%%timeit 
get_valid_keys_intersection()
# 64.5 ms ± 4.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit 
get_valid_keys_list_comp()
# 6.14 s ± 457 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

原始回答

我不确定 pandas 是否有内置函数来处理这个问题,但是你可以使用 Python 列表推导式来过滤有效的索引,类似于以下方式。

给定一个 DataFrame df2

           A    B       C   D    F
test    1.0 2013-01-02  1.0 3   foo
train   1.0 2013-01-02  1.0 3   foo
test    1.0 2013-01-02  1.0 3   foo
train   1.0 2013-01-02  1.0 3   foo
你可以使用这个来过滤你的索引查询。
keys = ['test', 'train', 'try', 'fake', 'broken']
valid_keys = [key for key in keys if key in df2.index.values]
df2.loc[valid_keys]

如果您使用 df2.columns 而不是 df2.index.values,这也适用于列。


1
尽管这个方法可行,但是在 pandas 中没有其他更好的方法吗?如果你需要处理一个大表格和一个大列表(n**2),这可能会非常耗费资源。 - Alex Lenail
@AlexLenail 很好的想法;我花了一点时间挖掘,找到了另一种解决方案。 - Josh
1
我希望有一个宏来执行 df.loc[df.index.intersection(list)] - Alex Lenail

2
我找到了一种替代方法(前提是先进行df.empty的检查)。你可以像这样做:

df[df.index=='2'] -> 返回匹配值的数据帧或空数据帧。

改进翻译如下:
如果您事先进行了df.empty的检查,那么我找到了一种替代方法。您可以尝试以下操作:

df[df.index=='2'] -> 将返回与之匹配的数据帧或空数据帧。


似乎扫描列比使用键值索引检索要慢得多?对于一个有112k行的数据框,操作符比较的平均时间约为508微秒,而try/except方法则要快得多,约为35.7微秒。 - ghukill

1

对我来说看起来工作得很好。我正在运行Python 3.5和pandas版本0.20.3。

import numpy as np
import pandas as pd

# Create dataframe
data = {'distance': [0, 300, 600, 1000],
        'population': [4.8, 0.7, 6.4, 2.9]}
df = pd.DataFrame(data, index=['Alabama','Alaska','Arizona','Arkansas'])

keys = ['Alabama', 'Alaska', 'Arizona', 'Virginia']

# Create a subset of the dataframe.
df.loc[keys]
          distance  population
Alabama        0.0         4.8
Alaska       300.0         0.7
Arizona      600.0         6.4
Virginia       NaN         NaN

或者,如果您想排除NaN行:

df.loc[keys].dropna()
          distance  population
Alabama        0.0         4.8
Alaska       300.0         0.7
Arizona      600.0         6.4

是的,这个解决方案也可以工作,但如果你恰好已经有了所有“NaN”的行,它们将会被意外地删除。 - Josh
1
我收到了一个FutureWarning警告: “将缺失标签的列表传递给.loc或[]将来会引发KeyError,您可以使用.reindex()作为替代方法。” - Tal Weiss
@TalWeiss 你使用的 pandas 版本是哪个? - stevepastelan
@stevepastelan 0.24.2 - Tal Weiss

1

1

有一个get函数,它允许你在找不到键时提供一个默认值。

import pandas as pd

pd.DataFrame([1], index=['1']).get('2', "default") 
Out[1]: 'default'
pd.DataFrame([1], index=['1']).get(['2'], "default") 
Out[2]: 'default'

1
使用@binjip答案中的示例数据框:
import numpy as np
import pandas as pd

# Create dataframe
data = {'distance': [0, 300, 600, 1000],
        'population': [4.8, 0.7, 6.4, 2.9]}
df = pd.DataFrame(data, index=['Alabama','Alaska','Arizona','Arkansas'])

keys = ['Alabama', 'Alaska', 'Arizona', 'Virginia']


从数据框中获取匹配的记录。注意:这只有在数据框索引唯一时才有效!
df.reindex(keys)

          distance  population
Alabama        0.0         4.8
Alaska       300.0         0.7
Arizona      600.0         6.4
Virginia       NaN         NaN

如果您想省略缺失的键:
df.reindex(df.index.intersection(keys))

         distance  population
Alabama         0         4.8
Alaska        300         0.7
Arizona       600         6.4

0

df.loc使用索引(来自df.index的值)而不是行的位置。您是否意味着使用.iloc


这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Code Different

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接