【问题标题】:How to match opposite values in data frame?如何匹配数据框中的相反值?
【发布时间】:2019-12-05 06:52:33
【问题描述】:

我基本上想从导入的数据框中消除相反的数量。

我的解决方案是构建一个新的数据框,忽略成对组合的总和为零的行。

考虑以下数据框:

df = pd.DataFrame([
    ['31/07/17', 43020500, 13552.65],
    ['31/07/17', 43020500, 13552.65],
    ['31/07/17', 43020500, 13552.65],
    ['31/07/17', 43020500, 13552.65],
    ['31/08/17', 43020500, 241024.48],
    ['31/08/17', 43020500, 241024.48],
    ['31/08/17', 43020500, 241024.48],
    ['31/08/17', 43020500, 241024.48],
    ['31/08/17', 43020500, 241024.48],
    ['31/08/17', 43020500, -13552.65],
    ['31/08/17', 43020500, -13552.65],
    ['31/08/17', 43020500, -13552.65],
    ['31/08/17', 43020500, -13552.65],
    ['31/08/17', 43020500, -13552.65],
    ['30/06/17', 43020500, 133540.13],
], columns = ['Data', 'Account','Amount']
)

df
Out[34]: 
        Data   Account     Amount
0   31/07/17  43020500   13552.65
1   31/07/17  43020500   13552.65
2   31/07/17  43020500   13552.65
3   31/07/17  43020500   13552.65
4   31/08/17  43020500  241024.48
5   31/08/17  43020500  241024.48
6   31/08/17  43020500  241024.48
7   31/08/17  43020500  241024.48
8   31/08/17  43020500  241024.48
9   31/08/17  43020500  -13552.65
10  31/08/17  43020500  -13552.65
11  31/08/17  43020500  -13552.65
12  31/08/17  43020500  -13552.65
13  31/08/17  43020500  -13552.65
14  30/06/17  43020500  133540.13

data frame example

预期结果是由索引 4 到 8、13 和 14 组成的新数据框,但我的代码无法正常工作...

import numpy as np
import pandas as pd

pd.options.display.float_format = '{:,.2f}'.format

df = pd.read_excel('ContractAssets_copy.XLSX')
df.sort_values('Date')

dfToList = df['Amount'].tolist()

newdf = []

def index(a_list, value):
    try:
        return a_list.index(value)
    except ValueError:
        return None

for number in dfToList:
    key = index(dfToList, dfToList[number] * -1)
    if key == None:
        newdf.append(df[number])

newdf

我该如何解决这个问题?

【问题讨论】:

    标签: python pandas list indexing find


    【解决方案1】:

    请注意,例如你有 413552.65 的值,但是 相反的值 (-13552.65) 是 5

    因此,如果每个值仅消除 一个 相反的值,那么在这种情况下为一个 应该留下负值(其他解决方案不尊重这一点 原则)。

    从定义一个函数开始,以消除“不需要的”行(从 当前行组):

    def eliminate(grp):
        grpSorted = grp.sort_values('Amount')
        amt = grpSorted.Amount
        nNeg = np.count_nonzero(amt.lt(0))
        nPos = amt.size - nNeg
        if nNeg == 0 or nPos == 0:  # No opposite values
            return grp
        vDiff = nNeg - nPos
        return grpSorted.head(vDiff) if vDiff > 0 else grpSorted.tail(-vDiff)
    

    然后添加AmountAbs栏:

    df['AmountAbs'] = df.Amount.abs()
    

    因为我们应该只根据 Amount 的绝对值对行进行分组。

    最后您可以进行所需的分组并将上述功能应用于 每组:

    df.groupby('AmountAbs').apply(eliminate)\
        .reset_index(level=0, drop=True)\
        .drop(columns=['AmountAbs'])
    

    上述指令中的“完成动作”涉及:

    • 从索引中删除 AmountAbs(仅保留原始索引),
    • 删除了 AmountAbs 列。

    如果您愿意,可以在上述指令中添加.sort_index(),以 恢复原来的行顺序。

    编辑

    还有更短的解决方案,无需创建任何辅助列 (并在最后删除)。

    消除功能略有不同:

    def elim(grp):
        grpSorted = grp.sort_values('Amount')
        amt = grpSorted.Amount
        nNeg = np.count_nonzero(amt.lt(0))  # No of negative values
        nPos = amt.size - nNeg              # No of positive values
        vDiff = nNeg - nPos
        return grpSorted.head(vDiff) if vDiff > 0 else grpSorted.tail(-vDiff)
    

    要应用它,运行:

    df.groupby(lambda x: abs(df.loc[x, 'Amount']))\
        .apply(elim).reset_index(level=0, drop=True)
    

    【讨论】:

      【解决方案2】:

      您可以尝试删除任何具有相反值的内容:

      df =df[~df['Amount'].isin(-1*df['Amount'])]
      
      df
      Out[36]: 
              Data   Account     Amount
      4   31/08/17  43020500  241024.48
      5   31/08/17  43020500  241024.48
      6   31/08/17  43020500  241024.48
      7   31/08/17  43020500  241024.48
      8   31/08/17  43020500  241024.48
      14  30/06/17  43020500  133540.13
      

      【讨论】:

        猜你喜欢
        • 2021-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-15
        • 1970-01-01
        • 1970-01-01
        • 2022-11-19
        • 1970-01-01
        相关资源
        最近更新 更多