首页 > 解决方案 > 通过元组列表过滤 DataFrame 的行

问题描述

假设我有以下 DataFrame:

 dic = {'a' : [1, 1, 2, 2, 2, 2, 3, 3, 3, 3],
'b' : [1, 1, 1, 1, 2, 2, 1, 1, 2, 2],
'c' : ['f', 'f', 'f', 'e', 'f', 'f', 'f', 'e', 'f', 'f'],
'd' : [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]}
df = pd.DataFrame(dic)

df
Out[10]: 
   a  b  c    d
0  1  1  f   10
1  1  1  f   20
2  2  1  f   30
3  2  1  e   40
4  2  2  f   50
5  2  2  f   60
6  3  1  f   70
7  3  1  e   80
8  3  2  f   90
9  3  2  f  100 

在下面,我想获取 a 和 b 列的值,其中 c='e' 并使用这些值来选择 df 的相应行(这将过滤第 2、3、6、7 行)。这个想法是创建一个元组列表并按该列表索引 df :

list_tup = list(df.loc[df['c'] == 'e', ['a','b']].to_records(index=False))
df_new = df.set_index(['a', 'b']).sort_index()

df_new
Out[13]: 
     c    d
a b        
1 1  f   10
  1  f   20
2 1  f   30
  1  e   40
  2  f   50
  2  f   60
3 1  f   70
  1  e   80
  2  f   90
  2  f  100

list_tup
Out[14]: [(2, 1), (3, 1)]

df.loc[list_tup]

导致 TypeError: unhashable type: 'writeable void-scalar',我不明白。有什么建议么?我对 python 和 pandas 很陌生,因此我认为我错过了一些基本的东西。

标签: pythonpandas

解决方案


我相信groupby().transform()在这个用例中使用布尔索引会更好:

valids = (df['c'].eq('e')                # check if `c` is 'e`
            .groupby([df['a'],df['b']])  # group by `a` and `b`
            .transform('any')            # check if `True` occurs in the group
                                         # use the same label for all rows in group
         )

# filter with `boolean indexing
df[valids]

输出:

   a  b  c   d
2  2  1  f  30
3  2  1  e  40
6  3  1  f  70
7  3  1  e  80

一个类似的想法,groupby().filter()它更具可读性但可能会稍微慢一些:

df.groupby(['a','b']).filter(lambda x: x['c'].eq('e').any())

推荐阅读