更改单行数值时保持总和约束行的比例性

3
我有一个由组成数据构成的数据集。每一列表示混合物中一个组分的百分比(小数值)。每一行总和为1。
如果混合物中的一个组分发生变化,其余组分必须相应地改变以满足总和约束条件。
我正在使用这些数据执行多元线性回归,并且需要进行一些转换,以使回归系数具有意义并可解释。数据集包含零值,这对我尝试实施的特定类型的转换是一个问题。
在我执行此转换之前,建议的操作是用一个小数字替换所有零值,并调整其余组分,使总和约束仍然得到满足。
您可以在下面的虚拟数据框中看到有些情况下一行中有多个零值。
data = {'X1': [0.21, 0.08, 0.57, 0.03],
        'X2': [0.27, 0.56, 0.0, 0.02],
        'X3': [0.0, 0.14, 0.0, 0.45],
        'X4': [0.13, 0.02, 0.26, 0.37],
        'X5': [0.39, 0.2, 0.17, 0.13]}

df = pd.DataFrame(data)

print(df)

让我们只考虑一行,因此计算公式如下:

假设原始值为r_i。对于组件r_i的变化delta_i,我们得到新值x_i

因此,x_i = r_i + delta_i

为了保持其余组件之间的相对比例,

r_j成为其余组件的原始值,

然后第j个分量的新值x_j为,

x_j = r_j - ((r_j / (1 - r_i) * delta_i) and i != j

我正在努力编写一个适当的循环来搜索数据集中的零值,然后在包含零值的索引和列中添加一个小数,然后继续使用上述公式调整整个行。

编辑:

抱歉数学公式表达不好。

对于虚拟df中的第一行,应用公式是直截了当的,因为该行只有一个零:

Table showing updated values of X1, X2, X4 and X5 when a small value is added to X3

重要的是剩余组件之间的相对比例保持不变,当我将零值更新为一个小数时,您可以在这里看到。

Relative proportions from the first row of the dummy df

对于虚拟数据框的第三行,情况变得有点复杂。我通过添加一个小数来更新第一个(X2)零值。第二个(X3)零值仍然为零,因为公式正在乘以和除以零。 因此,我进行了第二次更新,使X2和X3现在成为小的非零值,并在下面的表的第三行中显示。

Table showing updated values of X1, X4 and X5 when a small value is added to X2 and X3

在存在多个零的行上,保持剩余组件之间的相对比例的情况与此类似。

Relative proportions from the third row of the dummy df

我无法想出第一个问题的循环,更别提第二个了! 而且,不要担心相对比例表中由于除以小数而产生的大数字,我稍后会处理。


1
让这更好的是,基于上面的数据框架提供一个实际结果的示例。您可以在Excel中进行虚拟的前后对比,截屏并添加到您的帖子中。 - WGS
啊哈!编辑后,这使我的答案过时了。现在先删除并编辑。我想我现在明白你的意思了。 - WGS
你想通过降低幅度来保持元素分布吗? - kederrac
已编辑并显示与您的示例相同的结果。 - WGS
2个回答

1
这是编辑后的答案:
import pandas as pd
# To show 10 decimal points.
pd.options.display.float_format = '{:.10f}'.format

data = {'X1': [0.21, 0.08, 0.57, 0.03],
        'X2': [0.27, 0.56, 0.0, 0.02],
        'X3': [0.0, 0.14, 0.0, 0.45],
        'X4': [0.13, 0.02, 0.26, 0.37],
        'X5': [0.39, 0.2, 0.17, 0.13]}

df = pd.DataFrame(data)

delta_i = 0.000001
r_i = 0.0

# Provided formula.
def adjust_proportion(r_j, r_i, delta_i):
    return r_j - ((r_j / (1 - r_i)) * delta_i)

# For row-wise application.
def adjust_row(row, r_i, delta_i):

    # Get all zeros and their count in the row.
    zero_mask = (row == 0)
    zero_count = row[zero_mask].shape[0] # Get only x.

    # For every zero, adjust proportions for "cells" not in mask.
    for i in range(zero_count):
        row[~zero_mask] = row[~zero_mask].apply(lambda x: adjust_proportion(x, r_i, delta_i))

    # Increase the mask by delta_i across the board.
    row[zero_mask] += delta_i

    return row

# Apply ROW-WISE using axis=1.
df.apply(lambda x: adjust_row(x, r_i, delta_i), axis=1)
print(df)

# Check sums.
print(df.apply(lambda x: x.sum(), axis=1))

这将产生以下结果:

enter image description here

虽然还有更优化的方法,但这个方法应该可以处理一般逻辑。


我现在会尝试这个。 - Darius Nicholson
Jerome,你就像是巫师一样!在我编辑帖子的时间里,你已经解决了我的问题。已确认可与我的完整数据集配合使用,并保持所有其他组件之间的相对比例。谢谢! - Darius Nicholson
不客气。我将代码变得更易读而不是简洁,并添加了一些其他概念,以使其更加灵活。在未来,您可能希望针对特定值进行操作,因此r_idelta_i是可更改的。您现在也知道了apply,它只是一种将函数逐行或逐列(过于简化的定义,警告,哈哈)应用于数据框的方法。希望它能很好地工作。我也从事机器学习,特征工程很痛苦。 :) - WGS
我非常欣赏你让代码变得如此易读。我知道apply是正确的方法,但我无论如何也想不出如何使其工作。最终我避免使用它并尝试在循环中嵌套while函数以及嵌套循环等等。 我刚刚开始我的机器学习之旅,希望有机会交流。 - Darius Nicholson

1
你可以使用:

def exclude_zero(e, delta_i):
    """Replace zeros with a delta_i value by keeping the other non zeros value in the same distribution and total sum to 1"""

    zero_count = e.count(0)
    extra_amount = zero_count * delta_i

    for index, value in enumerate(e):
        if value == 0 :
            e[index] = delta_i
        else:
            e[index] = value * (1 - extra_amount)

    return e

data = {'X1': [0.21, 0.08, 0.57, 0.03],
        'X2': [0.27, 0.56, 0.0, 0.02],
        'X3': [0.0, 0.14, 0.0, 0.45],
        'X4': [0.13, 0.02, 0.26, 0.37],
        'X5': [0.39, 0.2, 0.17, 0.13]}

df = pd.DataFrame(data)

for index in range(len(df)):
    df.iloc[index] = exclude_zero(df.iloc[index].values.tolist(), 0.000001)

pd.options.display.precision = 8

df

enter image description here


@DariusNicholson 立刻检查 - kederrac
嗨rusu_ro1,感谢你的努力。很抱歉我回复你这么晚。在我运行Jerome的代码后,我出去了。你的代码也很好用!相对比例得到了保持。不过我注意到一件事情,就是当你达到10个小数位数时,你的代码和Jerome的代码开始有所不同。当然,这是微不足道的。总的来说,这是一个非常干净的解决方案。谢谢! - Darius Nicholson

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