【问题标题】:Filter a pandas dataframe using values from a dict使用字典中的值过滤熊猫数据框
【发布时间】:2015-12-08 13:59:49
【问题描述】:

我需要使用 dict 过滤数据框,其中键是列名,值是我要过滤的值:

filter_v = {'A':1, 'B':0, 'C':'This is right'}
# this would be the normal approach
df[(df['A'] == 1) & (df['B'] ==0)& (df['C'] == 'This is right')]

但我想在网上做点什么

for column, value in filter_v.items():
    df[df[column] == value]

但这会过滤数据框多次,一次一个值,而不是同时应用所有过滤器。有没有办法以编程方式做到这一点?

编辑:一个例子:

df1 = pd.DataFrame({'A':[1,0,1,1, np.nan], 'B':[1,1,1,0,1], 'C':['right','right','wrong','right', 'right'],'D':[1,2,2,3,4]})
filter_v = {'A':1, 'B':0, 'C':'right'}
df1.loc[df1[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]

给予

    A   B   C   D
0   1   1   right   1
1   0   1   right   2
3   1   0   right   3

但预期的结果是

    A   B   C   D
3   1   0   right   3

只应选择最后一个。

【问题讨论】:

  • 期望的输出是什么?
  • 只有同时对应所有条件的值的数据框。
  • 在pandas中应该有一个易于使用的命令,例如df.query_values(filter_v)

标签: python pandas


【解决方案1】:

IIUC,你应该可以这样做:

>>> df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).all(axis=1)]
   A  B      C  D
3  1  0  right  3

这可以通过制作一个系列来进行比较:

>>> pd.Series(filter_v)
A        1
B        0
C    right
dtype: object

选择df1的对应部分:

>>> df1[list(filter_v)]
    A      C  B
0   1  right  1
1   0  right  1
2   1  wrong  1
3   1  right  0
4 NaN  right  1

找到它们匹配的位置:

>>> df1[list(filter_v)] == pd.Series(filter_v)
       A      B      C
0   True  False   True
1  False  False   True
2   True  False  False
3   True   True   True
4  False  False   True

查找它们全部匹配的位置:

>>> (df1[list(filter_v)] == pd.Series(filter_v)).all(axis=1)
0    False
1    False
2    False
3     True
4    False
dtype: bool

最后使用它来索引 df1:

>>> df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).all(axis=1)]
   A  B      C  D
3  1  0  right  3

【讨论】:

  • 很好的解决方案和很棒的解释。
  • @DSM 如何检查来自dict.keysdf 列是否包含来自dict.valuesstr??
  • list(filter_v) 和 Series(filter_v) 的使用使这个解决方案变得晦涩难懂。为什么不直接使用df1[ filter_v.keys() ] == filter_v.values() 作为内部条件?
  • 我从来没有新的你可以用这样的系列来掩盖!这真是一个很棒的食谱。我认为应该在食谱中。
  • 这太棒了。
【解决方案2】:

这是一种方法:

df.loc[df[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]

更新:

如果各列的值相同,您可以执行以下操作:

# Create your filtering function:

def filter_dict(df, dic):
    return df[df[dic.keys()].apply(
            lambda x: x.equals(pd.Series(dic.values(), index=x.index, name=x.name)), asix=1)]

# Use it on your DataFrame:

filter_dict(df1, filter_v)

产量:

   A  B      C  D
3  1  0  right  3            

如果您经常这样做,您可以修补 DataFrame 以便轻松访问此过滤器:

pd.DataFrame.filter_dict_ = filter_dict

然后像这样使用这个过滤器:

df1.filter_dict_(filter_v)

这会产生相同的结果。

但是,这显然不是正确的方式。 我会使用 DSM 的方法。

【讨论】:

  • 谢谢,但这似乎也只应用了字典上的最后一个密钥对。
  • 不确定我明白你在说什么...这种方法是对所有在filter 字典中编码为键的列进行简单过滤,然后过滤行中的所有值以匹配字典。也许你可以用可重现的例子来说明这个问题?
【解决方案3】:

对于 python2,@primer 的回答是可以的。但是,由于 dict_keys,您应该在 Python3 中小心。例如,

>> df.loc[df[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]
>> TypeError: unhashable type: 'dict_keys'

Python3的正确方法:

df.loc[df[list(filter_v.keys())].isin(list(filter_v.values())).all(axis=1), :]

【讨论】:

    【解决方案4】:

    上面的抽象用于传递过滤器值数组而不是单个值的情况(类似于 pandas.core.series.Series.isin())。使用相同的示例:

    df1 = pd.DataFrame({'A':[1,0,1,1, np.nan], 'B':[1,1,1,0,1], 'C':['right','right','wrong','right', 'right'],'D':[1,2,2,3,4]})
    filter_v = {'A':[1], 'B':[1,0], 'C':['right']}
    ##Start with array of all True
    ind = [True] * len(df1)
    
    ##Loop through filters, updating index
    for col, vals in filter_v.items():
        ind = ind & (df1[col].isin(vals))
    
    ##Return filtered dataframe
    df1[ind]
    
    ##Returns
    
        A   B    C      D
    0   1.0 1   right   1
    3   1.0 0   right   3
    

    【讨论】:

    • 这种方法有效,即使 'vals' 是具有多个条件的列表。此外,我发现我可以使用生成的“ind”将静态值(或来自另一列的值)分配给原始数据框中的特定行,这很有用
    【解决方案5】:

    这是另一种方式:

    filterSeries = pd.Series(np.ones(df.shape[0],dtype=bool))
    for column, value in filter_v.items():
        filterSeries = ((df[column] == value) & filterSeries)
    

    这给出了:

    >>> df[filterSeries]
       A  B      C  D
    3  1  0  right  3 
    

    【讨论】:

      【解决方案6】:

      要跟进 DSM 的回答,您还可以使用 any() 将查询转换为 OR 运算(而不是 AND):

      df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).any(axis=1)]

      【讨论】:

        【解决方案7】:

        我遇到了一个问题,因为我的字典对同一个键有多个值。

        我能够将 DSM 的查询更改为:

        df1.loc[df1[list(filter_v)].isin(filter_v).all(axis=1), :]
        

        【讨论】:

        • @Muhammad 感谢您的编辑。因为这是我的第一篇文章,所以我还不熟悉格式。
        猜你喜欢
        • 2019-04-04
        • 2021-12-25
        • 2014-02-25
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-17
        相关资源
        最近更新 更多