Python 3.x Python/pands仅当值不为0时才进行减法

Python 3.x Python/pands仅当值不为0时才进行减法,python-3.x,pandas,dataframe,subtraction,Python 3.x,Pandas,Dataframe,Subtraction,我从看起来像这样的数据开始,但有更多的行: 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

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

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”

我认为减法是造成问题的原因,但我不知道如何改变它使其起作用。如蒙协助,将不胜感激


提前感谢。

使用
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)

方法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上测试)

参考:


与另一个数据帧执行元素相乘。

如何使用列名?我经常减去的列不会是最后一列。@Kimmy例如,如果您需要“d”,请通过
df.loc[:,~df.columns.isin(['d'])]取消选择它。
我尝试使用:“normalizer=I;ct_vals=df.loc[:,~df.columns.isin(normalizer)]=0.sub(df[normalizer],0.fillna(0)”,并得到了错误“TypeError:只允许将类似列表的对象传递给isin(),您传递了一个[str]”键入列名会出现以“KeyError:'i'”结尾的几个错误。@Kimmy您需要类似列表的对象,只允许将类似列表的对象传递给isin()。您可以使用选择名称列表所需的列。df_ref=df[columnNames]。然后应用第一个方法。columnNames是一个字符串列表,例如,['a'、'b'、'c'、'd']。对于缺少的列,我不确定是否要将这些缺少的列保留为nan或将它们转换为0。当我运行方法1时,它会给我没有列I的原始表。没有减去任何内容。当我运行方法2时,我得到错误“ValueError:无法在未指定级别且没有重叠名称的情况下加入“@Kimmy该方法无法在适当的位置执行此操作。它创建了一个新的df。您可以尝试使用类似于
pd.concat
的方法将2个df粘合在一起。经过一些修补,我得到了方法1。我无法让方法2发挥作用。这是我使用的代码:
normalizer=I normalizer\u col=df[normalizer]df\u temp=df.copy()df\u temp.drop(normalizer,axis=1,inplace=True)final\u vals=df\u temp.sub(normalizer\u col,axis=0)。乘法(df\u temp>0)。添加(0)
。谢谢你的帮助。很好。顺便说一句,你可以使用del-df[normalizer]删除一列。我认为对于方法2,您仍然可以将结果保存到临时df,如
df\u temp
。然后更改列名,并使用
df
%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)