Python 更改单行值时保持和约束行的比例

Python 更改单行值时保持和约束行的比例,python,pandas,loops,numpy,math,Python,Pandas,Loops,Numpy,Math,我有一个由合成数据组成的数据集。 每列代表整个混合物中成分的百分比(十进制值)。 每行总和为1 如果混合物中的一种成分发生变化,则其他成分必须相应地发生变化,以满足总和约束 我正在使用这些数据执行多元线性回归,它需要一些转换,以便回归系数有意义且可解释。数据集包含零值,这是我试图实现的特定类型转换的一个问题 在执行此转换之前,建议的操作是用一个小数字替换所有零值,并调整剩余的组件,以便仍然满足求和约束 您可以在下面的虚拟df中看到,在某些情况下,一行中有多个零值 data={'X1':[0.21

我有一个由合成数据组成的数据集。 每列代表整个混合物中成分的百分比(十进制值)。 每行总和为1

如果混合物中的一种成分发生变化,则其他成分必须相应地发生变化,以满足总和约束

我正在使用这些数据执行多元线性回归,它需要一些转换,以便回归系数有意义且可解释。数据集包含零值,这是我试图实现的特定类型转换的一个问题

在执行此转换之前,建议的操作是用一个小数字替换所有零值,并调整剩余的组件,以便仍然满足求和约束

您可以在下面的虚拟df中看到,在某些情况下,一行中有多个零值

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(数据)
打印(df)

让我们只考虑一行,所以这样做的公式如下:

将原始值设为
r\u i
。对于
delta\u i
的分量
r\u i
的变化,我们得到新值
x\u i

所以,
x_i=r_i+delta_i

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

r_j
为剩余组件的原始值

第j个分量x_j的新值为

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

我正在努力为这个问题编写一个合适的循环,该循环将搜索数据集中的零值,然后向索引和列中添加一个小数字 包含零值,然后使用上面描述的公式调整整行

编辑:

很抱歉数学公式的表达不好

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

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

对于虚拟df中的第三行,事情变得有点复杂。我通过添加一个小数字来更新第一个(X2)零值。第二个(X3)零值保持为零,因为公式是乘零和除零。 所以我做了第二次更新,使得X2和X3现在是小的非零值,这显示在下表的第三行

对于行中存在多个零的情况,保持其余组件之间的相对比例也是如此

我想不出第一个问题的循环,更不用说第二个问题了!
另外,不要担心用相对比例表中的一个小数字除以所产生的大数字,我稍后会处理这个问题。

以下是答案:

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))
这将产生以下结果:

有更多的最佳方法,但这应该考虑到一般逻辑。

您可以使用:

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

让这更好的是基于上述数据框的实际结果示例。你可以在Excel中前后做一个模拟,截屏,然后添加到你的帖子中。啊哈!编辑后,这就废弃了我的答案。现在要删除并编辑。我想我现在明白你的意思了。你想通过降低magnitude?已编辑,现在显示的结果与您的示例相同。我现在试试。Jerome您是某种向导!在我编辑我的帖子时,您已经解决了我的问题。已确认使用我的完整数据集,并保留了所有其他组件之间的相对比例。谢谢!不客气。我制作了代码如果你注意到它的灵活性,那么它的可读性要比简明性强一点,并加入了一些其他的概念。接下来,你可能希望针对特定的值,这样
r_i
delta_i
是可以更改的。你现在也知道了
apply
,这只是一种按行或列应用函数的方法(过于简单的定义,警告,lol)到您的数据帧。希望它能很好地工作。我也和ML一起工作,功能工程是痛苦的。:)我真的很欣赏你编写的代码的可读性。我知道
apply
是一条路,但我一辈子都想不出如何让它工作。我最终避免了它,并尝试了嵌套在循环中的函数嵌套在循环中,等等。我才刚刚开始我的ML之旅,希望有一天会遇到一些问题。@DariusNicholson checknowHi rusu_ro1,谢谢你的努力。很抱歉,我花了这么长时间才回复你。在我运行Jerome的代码后,我出去了。你的代码也运行得很好!保持了相对比例。不过我注意到,在你的代码和Jerome的代码之间,一旦你达到大约10位小数点后,数字就会开始不同。当然,这是正确的无足轻重。总而言之,这是一个非常干净的问题解决方案。谢谢!