【问题标题】:Python/Pandas Subtract Only if Value is not 0Python/Pandas 仅当值不为 0 时才减去
【发布时间】:2018-01-01 21:04:52
【问题描述】:

我从看起来像这样的数据开始,但行数更多:

Location  Sample  a     b     c     d     e     f     g     h     i
1         w       14.6  0     0     0     0     0     0     0     16.8
2         x       0     13.6  0     0     0     0     0     0     16.5
3         y       0     0     15.5  0     0     0     0     0     16.9
4         z       0     0     0     0     14.3  0     0     0     15.7
...

数据由前两列索引。我需要从 a - h 中的每个值中减去 i 列中的值,在每个原始列的数据框右侧添加一个新列。但是,如果第一列中有零,我希望它保持为零而不是减去。例如,如果我的代码有效,我将以下列添加到右侧的数据框中

Location  Sample  ...  a2    b2    c2    d2    e2    f2    g2    h2 
1         w       ...  -2.2  0     0     0     0     0     0     0
2         x       ...  0     -2.9  0     0     0     0     0     0
3         y       ...  0     0     -1.4  0     0     0     0     0
4         z       ...  0     0     0     0     -1.4  0     0     0
...

如果当前列中的值不为零,我正在尝试使用 pandas 中的 where 仅减去 i 列中的值,使用以下代码:

import pandas as pd

normalizer = i
columns = list(df.columns.values)

for column in columns:
    if column == normalizer: continue
    newcol = gene + "2"
    df[newcol] = df.where(df[column] == 0, 
                df[column] - df[normalizer], axis = 0)

我正在使用 for 循环,因为列数并不总是相同,并且被减去的列将使用不同的数据集具有不同的名称。

我收到此错误:“ValueError:错误的项目数通过了 9,位置意味着 1”。

我认为减法是导致问题的原因,但我不知道如何更改它以使其正常工作。任何帮助将不胜感激。

提前致谢。

【问题讨论】:

    标签: python-3.x pandas dataframe subtraction


    【解决方案1】:

    方法 1(相当快:比方法 2 快大约 3 倍)
    1. 选择相关的列
    2.做减法
    3. 使用在减法之前构造的 0、1 矩阵进行元素乘法。 (df_ref > 0) 中的每个元素如果最初为 0,则为 0,否则为 1。

    ith_col = df["i"]
    subdf = df.iloc[:, 2:-1]  # a - h columns 
    df_temp = subdf.sub(ith_col, axis=0).multiply(subdf > 0).add(0)
    df_temp.columns = ['a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2'] # rename columns
    df_desired = pd.concat([df, df_temp], axis=1)
    

    注意在这个方法中,0 是负数。因此,我们最后多了一个add(0)。是的,0 可以是负数。 :P

    方法2(更具可读性)
    1.找到大于0的部分有条件。
    2. 选择相关的行
    3. 减法
    4.填0。

    ith_col = df["i"]
    df[df > 0].iloc[:,2:-1].sub(ith_col, axis=0).fillna(0)
    

    第二种方法与@Wen 的回答非常相似。归功于他:P

    两种方法的速度比较(在 Python 3 和 pandas 0.20 上测试)

    %timeit subdf.sub(ith_col, axis=0).multiply(subdf > 0).add(0)
    688 µs ± 30.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    %timeit df[df > 0].iloc[:,2:-1].sub(ith_col, axis=0).fillna(0)
    2.97 ms ± 248 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    参考:

    DataFrame.multiply 与另一个数据帧执行元素乘法。

    【讨论】:

    • 您可以使用名称列表选择所需的列。 df_ref = df[列名]。然后应用第一种方法。 columnNames 是一个字符串列表,例如 ['a','b','c','d']。对于缺失的列,我不确定你是要保留这些缺失的 nan 还是将它们变成 0。
    • 当我运行方法 1 时,它给了我没有第 i 列的原始表。什么都没有被减去。当我运行方法 2 时,我收到错误“ValueError: cannot join with no level specified and no overlap names”。
    • @Kimmy 该方法不会到位。它创建一个新的df。您可以尝试使用 pd.concat 之类的方法将 2 个 df 粘合在一起。
    • 经过一些修补后,我得到了方法 1 的工作原理。我无法让方法 2 起作用。这是我使用的代码:normalizer=i normalizer_col=df[normalizer] df_temp=df.copy() df_temp.drop(normalizer, axis=1, inplace=True) final_vals=df_temp.sub(normalizer_col, axis=0).multiply(df_temp > 0).add(0)。感谢您的帮助。
    • Gald 它可以工作:P 顺便说一句,您可以使用 del df[normalizer] 删除一列。我认为对于方法 2,您仍然可以将结果保存到临时 df,例如 df_temp。然后更改列名,并与df 连接
    【解决方案2】:

    使用mask + fillna

    df.iloc[:,2:-1]=df.iloc[:,2:-1].mask(df.iloc[:,2:-1]==0).sub(df['i'],0).fillna(0)
    df
    Out[116]: 
       Location Sample    a    b    c    d    e    f    g    h     i
    0         1      w -2.2  0.0  0.0  0.0  0.0  0.0  0.0  0.0  16.8
    1         2      x  0.0 -2.9  0.0  0.0  0.0  0.0  0.0  0.0  16.5
    2         3      y  0.0  0.0 -1.4  0.0  0.0  0.0  0.0  0.0  16.9
    3         4      z  0.0  0.0  0.0  0.0 -1.4  0.0  0.0  0.0  15.7
    

    更新

    normalizer = ['i','Location','Sample']
    df.loc[:,~df.columns.isin(normalizer)]=df.loc[:,~df.columns.isin(normalizer)].mask(df.loc[:,~df.columns.isin(normalizer)]==0).sub(df['i'],0).fillna(0)
    

    【讨论】:

    • 如何使用列名来代替?我经常减去的那一列不会是最后一列。
    • @Kimmy 例如,如果您需要 'd',请取消选择 df.loc[:,~df.columns.isin(['d'])]
    • 我尝试使用:“normalizer = i ; ct_vals = df.loc[:, ~df.columns.isin(normalizer)].mask(df.loc[:, ~df.columns.isin (normalizer)]==0).sub(df[normalizer],0).fillna(0)",并得到错误“TypeError: only list-like objects are allowed to be passed to isin(), you pass a [str]"
    • 键入列名也会产生一些以“KeyError: 'i'”结尾的错误。
    • @Kimmy 你需要类似列表,只允许将类似列表的对象传递给 isin()
    猜你喜欢
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 2022-01-11
    • 2023-01-24
    • 1970-01-01
    • 2020-02-05
    相关资源
    最近更新 更多