自定义算法来处理 DataFrame 中的负值

Custom algorithm to deal with negative values within a DataFrame

首先,我有一个如下所示的 DataFrame:

df = pd.DataFrame({'a': [25, 22, -2, 16, 10], 'b': [-5, 18, -2, 25, 48], 'c': [34, -12, 7, 8, 22],
'd': [10, 8, -2, -4, 12]})

目标:使用保留每列中负值影响的特定脚本或函数消除所有零。

我正在尝试开发一种方法来查看数据框,找到负值并取负值的绝对值并加一。本质上,这会将 DataFrame 中的每个负值替换为正值 1。

接下来,我想减去我在取负数的绝对值(加一)后计算的值,然后从下一行值(同一列内)中减去它。

另外: 在负值后面的值也是负数的情况下,我想对两个负值都做同样的操作,但是我想减去绝对值加一的总和,对于每个负数,并从下一个减去正排。如果在我想从之后的行中减去之后,修正后的负值之后的行值变得小于 1,直到负值消失并且它们之后的行都小于 1。

预期的输出将有望帮助理解我打算做什么:

expected_output = pd.DataFrame({'a': [25, 22, 1, 13, 10], 'b': [1, 12, 1, 22, 48],
'c': [34, 1, 1, 1, 22],'d': [10, 8, 1, 1, 4]})

我可以用负值的绝对值加一来代替负值,使用:

df[df < 0] = abs(df[df < 0] + 1)

我也知道我可以使用以下方法找到负值的位置:

neg_loc = df.loc[df['a'] < 0].index

现在我使用以下方法找到负值后的值:

row_after_neg = df['a'].iloc[neg_loc + 1]

最后,我可以将负值的绝对值加一添加到负值之后的行:

total = row_after_neg.add(abs(neg_loc  + 1))

所以,我的问题是如何将它们拼接在一起,以便它遍历整个 DataFrame 并执行我指定的操作。

提前感谢您的 advice/help!

你的问题有点令人困惑,所以我希望我能解决所有的要求,如果没有,请在评论中告诉我。我选择使用 for 循环,因为您想进行逐行比较。如果速度是一个问题,我会避免使用 for 循环,看看你是否可以留在熊猫的架构中。

设置:

import pandas as pd
df = pd.DataFrame({'a': [25, 22, -2, 16, 10], 'b': [-5, 18, -2, 25, 48], 'c': [34, -12, 7, 8, 22],
'd': [10, 8, -2, -4, 12]})

创建一个具有 abs(负值)+ 1 和 0 的数据框版本以替换正值的 nans

pos_df = (abs(df[df < 0]) + 1).fillna(0)

For 循环从第二行开始遍历数据帧:

for index, row in df.iloc[1:,:].iterrows():

然后用正数据帧的前一行减去数据帧的行

df.loc[index] = row - pos_df.loc[index - 1]

然后您重新计算 pos_df 的行,因为您想要检查是否有任何数字变为负数。需要注意的是,我正在切换 df 数据帧而不是 pos_df 中的所有负值。

pos_df.loc[index][df.loc[index] < 0] = (abs(df.loc[index][df.loc[index] < 0]) + 1).fillna(0)

最后将所有负值更改为 1:

df[df < 0] = 1

完整代码如下:

import pandas as pd
df = pd.DataFrame({'a': [25, 22, -2, 16, 10], 'b': [-5, 18, -2, 25, 48], 'c': [34, -12, 7, 8, 22],
'd': [10, 8, -2, -4, 12]})
pos_df = (abs(df[df < 0]) + 1).fillna(0)

for index, row in df.iloc[1:,:].iterrows():
    df.loc[index] = row - pos_df.loc[index - 1]
    pos_df.loc[index][df.loc[index] < 0] = (abs(df.loc[index][df.loc[index] < 0]) + 1).fillna(0)

df[df < 0] = 1

最终输出为:

    a       b       c       d
0   25.0    1.0     34.0    10.0
1   22.0    12.0    1.0     8.0
2   1.0     1.0     1.0     1.0
3   13.0    22.0    1.0     1.0
4   10.0    48.0    22.0    4.0

希望对您有所帮助!

编辑:

所以代码:

pos_df.loc[index][df.loc[index] < 0] = (abs(df.loc[index][df.loc[index] < 0]) + 1).fillna(0)

是一个比较复杂的pandas表达式。一点上下文,在 Pandas 中有 seriesdataframes,您可以将 series 视为数据框的一列或一行。当您在数据框中执行条件 selection 时,数据框会保持其形状,不符合条件的值显示为 Nan。使用系列,您只会获得满足条件的值。

这是一个例子:

df[df == 1]
series[series == 1]


   a       b       c       d
0   Nan    Nan    Nan    Nan
1   Nan    Nan    1.0    Nan
2   Nan    Nan    1.0    1.0
3   Nan    Nan    1.0    1.0
4   Nan    Nan    Nan    Nan

c
1.0
1.0
1.0

因此,正如您在上面看到的,该系列的形状从 [5,1] 变为 [3,1]。现在回到代码。 pos_df.loc[index] selects 我们感兴趣的行来自数据框,其中包含转换后的负数。如果您将其视为:

,可能更容易概念化
pos_s = pos_df.loc[index]
s = df.loc[index]

pos_s[s < 0] = (abs(s[s < 0]) + 1).fillna(0)

因此,正如您在上面看到的,这与您在问题中使用的表达式相同,但在 for 循环目前正在迭代的行上执行,而不是在整个数据帧上执行。在代码中:

(abs(s[s < 0]) + 1).fillna(0)

我正在查找数据框中所有曾经或已经变成负数的值并重新转换它们。然后我得到一个像这样的系列:

c
13
12
4

因为这是一个系列,您会注意到形状是 [3,1] 而不是预期的 [5,1]。所以为了避免弄乱数据框,我必须只替换负值。所以我使用代码:

pos_s[s < 0]

我正在 select 计算 df 行中的所有负数,所以输出是

[False, True, True, False, True] 

然后我将此条件应用到 pos_df 的行到 select 第二个、第三个和第五个值并更新它们,以防任何最初的正值一旦减去就变成负值。