Pandas:根据年份对数据框应用不同的过滤器

Pandas: Apply different filter to dataframe based on the year

如果年份高于或低于某个范围,我想对我的数据框应用不同的过滤器。这是数据框

dataset=pd.DataFrame({'ID': [1,1,1,2,2,2,3,3,3,4,4,4,5,5,5], 
                      'Avail' : [2017,2017,2017,2018,2018,2018,2017,2017,2017,2017,2017,2017,2017,2018,2018], 
                      'Change' : [0,0,2018,0,0,0,0,0,0,0,0,0,2018,0,0],
                      'Pref' : [1,2,3,1,2,3,1,2,3,1,2,3,1,2,3],
                      'Status': ['null', 'null','Q','null','null','null','Q','null','null','null','null','null','Q','null','null']
                      },columns=['ID', 'Avail', 'Change', 'Pref', 'Status'])

这是我编写的产生错误的代码:

def yearfilt(x):
    if x.loc[:, ['Avail', 'Change']].values.max(axis=1) < 2018:
        if pd.isnull(x.Status):
            x.drop_duplicates(subset=['STU_ID','Status' ], keep='last')
        else:
            x=x.drop(x[pd.isnull(x.Status)].index)
    else:
        if pd.isnull(x.ASSESSMENT_OUTCOME_CD):
            x.drop_duplicates(subset=['STU_ID','Status' ], keep='first')
        else:
         x=x.drop(x[pd.isnull(x.Status)].index)

df=dataset.groupby(['ID']).apply(yearfilt).sort_values(["ID"]).reset_index(drop=True)

错误是

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

我想执行的是:

If the max (Avail, Change) < 2018 then
Case 1: the same status --> drop duplicates and keep the last
Case 2: different status --> drop null-value statuses

else (in other words max (Avail, Change) = 2018)
Case 1: the same status --> drop duplicates and keep the first
Case 2: different status --> drop null-value statuses

输出应如下所示:

ID  Year  Change  Pref  Status
1   2017   2018    3      Q
2   2018   0       1     null
3   2017   0       1      Q
4   2017   0       3     null
5   2017   2018    1      Q

基本上,每个 ID 我只想保留一个。 谢谢

您看到的 ValueError 是因为您正在尝试检查 if(some_series)。我不确定哪一行给出了您指出的错误,但看起来您的任何 if 语句都可能导致此问题。

例如,第一个 if 语句将一系列值与单个值进行比较。结果是一系列布尔值,而不是 if 语句可以理解的单个 True/False。 pd.isnull.

也可能发生同样的情况

您应该检查您的哪些命令给出了数组结果,并考虑它如何符合您的代码逻辑。

如果我正确理解你的问题,这里有一个可能的解决方案:

def yearfilt(group):
    # Apply .max() twice to get a single value across the group.
    # Otherwise the results is a Series, and using if will result in a ValueError.
    if group[['Avail', 'Change']].max().max() < 2018:
        # Returns true if there is a unique status value.
        if group['Status'].unique().shape[0] == 1:
            # Return last row as a dataframe.
            return group.iloc[-1:]
        else:
            # Return ALL rows with status not null (may be more than 1?).
            return group[group['Status'] != 'null']
    else:
        if group['Status'].unique().shape[0] == 1:
            # Return first row as a dataframe.
            return group.iloc[:1]
        else:
            return group[group['Status'] != 'null']

dataset.groupby('ID').apply(yearfilt).reset_index(drop=True)

要记住几件事:

  • 传递给您在 groupby().apply 中使用的函数的每个参数都传递给整个数据帧的一个子集。您需要 return 新对象,而不是修改您的函数接收的组。
  • 如果您使用 isnull,您尝试过滤的值必须是 None,而不是字符串 'null''None''nan'等。参见 the docs 缺失值。
  • 您不能在 Series 上使用 if 语句,只能使用一个值。