Python Pandas使用groupby来识别行

3

我曾经使用SAS清洗数据,但现在想转用Python。

我有一个大型数据集,是从一些文件(html)中抓取的,但其中包含了一些噪音信息,我希望能够去除这些无关数据。

基本上,我需要在满足条件为True的行后删除某些数据行(但这可能是一个列表,有多个True或者根本没有True;如果有True,则要确定最后一个True)。

原始数据:

<table>
  <tr>
    <td>Report_ID</td>
    <td>Table_ID</td>
    <td>Group_ID</td>
    <td>Item_ID</td>
    <td>Flag_old</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item1</td>
    <td>0</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item2</td>
    <td>0</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item3</td>
    <td>1</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item4</td>
    <td>0</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item5</td>
    <td>0</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item1</td>
    <td>1</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item2</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item3</td>
    <td>1</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item4</td>
    <td>0</td>
  </tr>
        <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item1</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item2</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item3</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item4</td>
    <td>0</td>
  </tr>
</table>

预期数据:

<table>
  <tr>
    <td>Report_ID</td>
    <td>Table_ID</td>
    <td>Group_ID</td>
    <td>Item_ID</td>
    <td>Flag_old</td>
    <td>Flag_new</td>
  </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item1</td>
    <td>0</td>
    <td>0</td>    
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item2</td>
    <td>0</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item3</td>
    <td>1</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item4</td>
    <td>0</td>
    <td>1</td>
    </tr>
        <tr>
    <td>A</td>
    <td>1</td>
    <td>1</td>
    <td>item5</td>
    <td>0</td>
    <td>1</td>
    </tr>
  <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item1</td>
    <td>1</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item2</td>
    <td>0</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item3</td>
    <td>1</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>2</td>
    <td>item4</td>
    <td>0</td>
    <td>1</td>
  </tr>
        <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item1</td>
    <td>0</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item2</td>
    <td>0</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item3</td>
    <td>0</td>
    <td>0</td>
  </tr>
    <tr>
    <td>A</td>
    <td>1</td>
    <td>3</td>
    <td>item4</td>
    <td>0</td>
    <td>0</td>
  </tr>
</table>

如上所示,我想识别Flag_old == 1之下的行。
鉴于数据结构,我首先使用groupby对整个dataframe进行分段,然后考虑定义一个函数来选择行并将函数应用于dataframe groupby对象,然后为整个dataframe创建一个新列以指示这些嘈杂数据的行。
def lastline(series):
    return max(series[series.values == 1].index)

df['lastline'] = df.groupby('id').apply(lastline(df['flag']))

但是我遇到了'int' object is not callable的错误。

请问您能否指导我如何正确处理?我已经为此苦苦挣扎了几天...非常感谢。


2
你在脚本的其他地方调用了变量 lastlinemax 吗?如果没有提供 最小完整可验证示例,很难知道发生了什么。 - Mr. T
请分享您的数据样本和期望输出。 - AndreyF
@AndreyF,谢谢你们两个,我会生成一个样本数据。 - Jin
@Piinthesky,我没有打电话给他们。我将在一分钟内生成样本数据,这样你们就可以看到我想要如何处理我的数据了。再次感谢。 - Jin
@AndreyF 在这种情况下,我只能获取flag_old == 1的行,对吗?但是我想要识别以下行... - Jin
2个回答

1

我认为你需要使用transform自定义函数来返回新的列:

def f(x):
    #get cumulative sum, shift
    a = x.cumsum().shift()
    #check max value of cumsumed a and chain condition for remove 0 only groups
    #convert Trues to 1 by astype
    return ((a == a.max()) & (a != 0)).astype(int)

df['Flag_new'] = df.groupby('Group_ID')['Flag_old'].transform(f)
print (df)
   Report_ID  Table_ID  Group_ID Item_ID  Flag_old  Flag_new
0          A         1         1   item1         0         0
1          A         1         1   item2         0         0
2          A         1         1   item3         1         0
3          A         1         1   item4         0         1
4          A         1         1   item5         0         1
5          A         1         2   item1         1         0
6          A         1         2   item2         0         0
7          A         1         2   item3         1         0
8          A         1         2   item4         0         1
9          A         1         3   item1         0         0
10         A         1         3   item2         0         0
11         A         1         3   item3         0         0
12         A         1         3   item4         0         0

非常感谢,能否再提供一些详细信息?我理解你的函数是创建一个由零填充的数组,但我不理解接下来的步骤... - Jin
你认为 a[-1] = x.max() 吗?a[-1] 用于选择数组 a 的最后一个值,而 a[-1] = x.max()x 的最大值赋给了最后一个值。 - jezrael
@Jin - 或者需要检查每组中最后一个先前的值,行索引为2,6,10吗? - jezrael
jezrael,请查看我的修订后的HTML表格,其中新增了item5,再次感谢您。 - Jin
我测试第一个值是否为1,并且需要删除bfill() - jezrael
显示剩余5条评论

0

这可能对于使用apply的一行代码有点复杂,但您可以使用:

df['flag_new'] = df.groupby("Group_ID").apply(lambda g_df: [0]* len(g_df['Flag_old']) if g_df['Flag_old'].sum() == 0 else [0]* (len(g_df['Flag_old'])-1) +[1]).apply(pd.Series).stack().reset_index(drop=True)

或者你可以使用 transform

df['flag_new'] = df.groupby("Group_ID")['flag'].transform(lambda x: [0]* len(x) if x.sum() == 0 else [0]* (len(x)-1) +[1])

在这两种情况下,输出将是相同的:
   Report_ID  Table_ID  Group_ID Item_ID  Flag_old  Flag_new
0          A         1         1   item1         0         0
1          A         1         1   item2         0         0
2          A         1         1   item3         1         0
3          A         1         1   item4         0         1
4          A         1         2   item1         1         0
5          A         1         2   item2         0         0
6          A         1         2   item3         1         0
7          A         1         2   item4         0         1
8          A         1         3   item1         0         0
9          A         1         3   item2         0         0
10         A         1         3   item3         0         0
11         A         1         3   item4         0         0

非常感谢您,AndreyF!我想要识别在“Flag_old”==1的行之后的行。在我的数据中,这些行不一定是最后一行,例如可能有多行?您能否给我建议如何处理?我正在考虑与这些行的索引进行比较... - Jin

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