pandas 查找并标记偏移panda Dataframe 行

8wtpewkr  于 2022-12-16  发布在  其他
关注(0)|答案(2)|浏览(116)

我有一个庞大的数据文件,其中列出了我们财务系统中的费用条目。(特定任务的唯一标识符)、dateamount。经常会出现错误,并且通过在同一天输入同一事项的抵销成本来“取消”费用。使问题复杂化的是,有时候,其中一个已取消的条目被重新输入。这导致一个事务处理有3行(原始、取消和重新输入)。我正在尝试标记哪些行正在取消其他行以及哪些行已被取消。
在下面的代码中,我编写了一个简单的函数,它遍历 Dataframe 的每一行,并将给定事件和日期的金额与其他金额进行比较,如果有其他金额抵消了正在检查的金额,它将标记该事件。

import pandas as pd
data = [
        [1,'1/2/2022',10],
        [1,'1/2/2022',15],
        [1,'1/2/2022',-10],
        [2,'1/4/2022',12],
        [2,'1/5/2022', 5],
        [2,'1/5/2022',-5],
        [2,'1/5/2022', 5]
    ]
df = pd.DataFrame(data, columns=['matter','date','amount'])

def rev_check(matter, date, WorkAmt, df):
    funcDF = df.loc[(df['matter'] == matter) & (df['date'] == date)] 
    listCheck = funcDF['amount'].tolist()
    if WorkAmt*-1 in listCheck:
        return 'yes'
    
df['reversal'] = df.apply(lambda row: rev_check(row.matter, row.date, row.amount, df), axis=1)

print(df)

产生以下输出:

matter      date  amount reversal
0       1  1/2/2022      10      yes
1       1  1/2/2022      15     None
2       1  1/2/2022     -10      yes
3       2  1/4/2022      12     None
4       2  1/5/2022       5      yes
5       2  1/5/2022      -5      yes
6       2  1/5/2022       5      yes

我被卡住的地方在第6行。该行不应该被标记,因为第4行的条目已经被第5行的条目颠倒了。
我很想知道如何更好地处理这一问题,这样我就不会标记这些重新输入的金额。

2w3rbyxf

2w3rbyxf1#

我承认我在理解@mozway的解决方案时遇到了一些麻烦。这里有一个我觉得更容易理解的替代方案。

# Steal clever technique of uniquely labelling
# repetitions of ['matter','date','amount']
df['occurrence'] = df.groupby(['matter','date','amount']).cumcount()

# Invert cancelling amounts for comparison in next step
df['abs_amount'] = df['amount'].abs()

# Mark items where cancels equals amount
df['reversal']   = df.duplicated(['matter', 'date', 'occurrence', 'abs_amount'],
                                 keep=False)

df现在是...

matter    date       amount   occurrence  abs_amount  reversal
0      1    1/2/2022       10            0          10      True
1      1    1/2/2022       15            0          15     False
2      1    1/2/2022      -10            0          10      True
3      2    1/4/2022       12            0          12     False
4      2    1/5/2022        5            0           5      True
5      2    1/5/2022       -5            0           5      True
6      2    1/5/2022        5            1           5     False
6l7fqoea

6l7fqoea2#

以下是一种通过重新塑造 Dataframe 的方法:

df2 = (df
  .assign(sign=np.sign(df['amount']), # sign
          abs=df['amount'].abs()      # absolute value
          # occurrence of the value
          n=df.groupby(['matter', 'date', 'amount']).cumcount(),
         )
  .reset_index()
  .pivot(index=['matter', 'date', 'n', 'abs'], columns='sign', values='index')
)

reversal = df2.where(df2.notna().all(axis=1)).stack()
df['reversal'] = df.index.isin(reversal)

输出:

matter      date  amount  reversal
0       1  1/2/2022      10      True
1       1  1/2/2022      15     False
2       1  1/2/2022     -10      True
3       2  1/4/2022      12     False
4       2  1/5/2022       5      True
5       2  1/5/2022      -5      True
6       2  1/5/2022       5     False

中级df2

sign                    -1    1
matter date     n abs          
1      1/2/2022 0 10   2.0  0.0  # both values, this is a reversal
                  15   NaN  1.0
2      1/4/2022 0 12   NaN  3.0
       1/5/2022 0 5    5.0  4.0  # both values, this is a reversal
                1 5    NaN  6.0  # this one is alone, not a reversal
变体

逻辑相同,但使用双groupby

df['reversal'] = (df
  .assign(sign=np.sign(df['amount']),
          n=df.groupby(['matter', 'date', 'amount']).cumcount(),
          abs=df['amount'].abs()
         )
  # if we have 2 values per group, this is a reversal
  .groupby(['matter', 'date', 'abs', 'n']).transform('size').eq(2)
 )

相关问题