Pandas:如何按多个条件选择行如果不是 nan 且等于特定值

问题描述

我有一个数据帧

index 街道 房子 建筑
1 ABC 20 a
2 ABC 20 b
3 ABC 21 NaN
4 BCD 2 1

需要从此 DataFrame 创建多项选择

  • If Street == str_filter;
  • 如果 House == house_filter;
  • If Building == build_filter AND If Building is not NULL;

我已经试过了df[(df['Street'] == str_filter) & (df['House'] == house_filter) & ((df['Building'] == build_filter) & (pd.notnull(df['Building'])))

但并没有导致我特别想看到的结果。我必须检查 Building 值是否不是 NaN,如果是真的,请选择具有特定 Building num 的行。但是,如果该行的建筑物具有 NaN 值但还满足其他条件,我也想选择该行。

一个想法是为过滤器值集和满足 pd.notnull 条件的值集创建列表:

filter_values = [str_filter,house_filter,build_filter] 
notnull_values = [pd.notnull(entry) for entry in filter_values]

这个不符合性能标准,因为我有非常大的 DataFrame 并且使用额外的过滤创建额外的列表会导致性能超群。可能的解决方案可能在于 df.loc 函数,但我不知道如何实现。

总而言之,问题如下:如何在带有 NaN 值条件的 Pandas 中创建多项选择

UPD:似乎我必须使用的函数df[... & (df['Building'] == 'a' if pd.notnull(df['Building']))] 使用与 lambda 应用技巧的类比

解决方法

既然你在评论中说你也有重复的问题,我已经添加了一个选项来删除这些。

我用这个重新创建了你的数据集,并添加了一个副本。

df = pd.DataFrame({
    "Street" : ["ABC","ABC","BCD","ABC"],"House" : [20,20,21,2,21],"Building" : ['a','b',np.NaN,1,np.NaN]
})

要从数据集中删除重复项,您只需运行此块:

df = df.drop_duplicates()

我假设您的过滤器可能是您想要包含在最终数据集中的字符串或数字的白名单。因此,我冒昧地定义了一个过滤器:

street_filter = ["ABC","BCD"]

要清理数据集,您可以使用此方法:

def get_mask(df,street_filter,house_filter,building_filter):
  not_na_mask = df['Building'].notna()
  # hopefully smaller df to query
  df = df[not_na_mask]

  street_mask = df['Street'].apply(lambda x: x in street_filter)
  df = df[street_mask]

  house_mask = df['House'].apply(lambda x: x in house_filter)
  df = df[house_mask]

  building_mask = df['Building'].apply(lambda x: x in building_filter)
  return df[building_mask]

示例

street_filter = ["ABC","BCD"]
house_filter = [20,21]
building_filter = ["a"]
get_mask(df,building_filter)

    Street  House   Building
 0  ABC     20      a
,

一个相当简单的方法是:

if df[df['Building'].isnull()].empty:
    df[df['Street'] == str_filter][df['House'] == house_filter][df['Building'] == build_filter]
else:
    df[df['Street'] == str_filter][df['House'] == house_filter][df['Building'].isnull()]

我不确定这是否能满足您的性能要求?