从Pandas DataFrame中减去第一行的值

9

I have a pandas dataframe:

a = pd.DataFrame(rand(5,6)*10, index=pd.DatetimeIndex(start='2005', periods=5, freq='A'))
a.columns = pd.MultiIndex.from_product([('A','B'),('a','b','c')])

我想要从 a 中减去行 a['2005']。为了做到这一点,我尝试了以下方法:
In [22]:

a - a.ix['2005']

Out[22]:
    A   B
    a   b   c   a   b   c
2005-12-31  0   0   0   0   0   0
2006-12-31  NaN     NaN     NaN     NaN     NaN     NaN
2007-12-31  NaN     NaN     NaN     NaN     NaN     NaN
2008-12-31  NaN     NaN     NaN     NaN     NaN     NaN
2009-12-31  NaN     NaN     NaN     NaN     NaN     NaN

显然这样做是不行的,因为在进行操作时pandas会将索引对齐。以下方法可以解决此问题:

In [24]:

pd.DataFrame(a.values - a['2005'].values, index=a.index, columns=a.columns)

Out[24]:
    A   B
    a   b   c   a   b   c
2005-12-31  0.000000    0.000000    0.000000    0.000000    0.000000    0.000000
2006-12-31  -3.326761   -7.164628   8.188518    -0.863177   0.519587    -3.281982
2007-12-31  3.529531    -4.719756   8.444488    1.355366    7.468361    -4.023797
2008-12-31  3.139185    -8.420257   1.465101    -2.942519   1.219060    -5.146019
2009-12-31  -3.459710   0.519435    -1.049617   -2.779370   4.792227    -1.922461

但我不想每次都要形成一个新的DataFrame来执行这种操作。我尝试了apply()方法,如下所示:a.apply(lambda x: x-a['2005'].values)但是我得到了ValueError: cannot copy sequence with size 6 to array axis with dimension 5所以我真的不知道该怎么办。有没有一种简单的方法可以做到这一点,我没有看到?我认为应该有一种简单的方法可以在原地完成这个操作,这样您就不必每次构建一个新的数据框。我还尝试了sub()方法,但减法只应用于第一行,而我想从数据框中的每一行中减去第一行。

请原谅我的新手,但是您所说的“pandas正在排列索引”是什么意思,为什么第一段代码会失败? - tumultous_rooster
Pandas使用索引“排列”操作,因为该操作仅适用于公共索引。因此,如果您想从DataFrame中的所有行中减去一行,则需要首先将其转换为numpy数组,如答案所示。 - pbreach
3个回答

12

使用 Pandas 对齐索引非常方便。因此,当您希望 Pandas 忽略索引时,需要删除索引。您可以通过将 DataFrame a.loc['2005'] 转换为一维 NumPy 数组来实现:

In [56]: a - a.loc['2005'].values.squeeze()
Out[56]: 
                   A                             B                    
                   a         b         c         a         b         c
2005-12-31  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000
2006-12-31  0.325968  1.314776 -0.789328 -0.344669 -2.518857  7.361711
2007-12-31  0.084203  2.234445 -2.838454 -6.176795 -3.645513  8.955443
2008-12-31  3.798700  0.299529  1.303325 -2.770126 -1.284188  3.093806
2009-12-31  1.520930  2.660040  0.846996 -9.437851 -2.886603  6.705391

squeeze 方法 可以将形状为 (1, 6) 的 NumPy 数组 a.loc['2005'] 转换为形状为 (6,) 的数组。这样可以使数组在减法期间被广播。


哦,我明白你的意思了。这就解释了为什么 a - a.loc['2005'].values[0] 也能起作用。 - pbreach
也许基于单词的操作应该增加一个ignore_index参数。 - DSM
@unutbu 你说得对,根据你所说的,squeeze方法是必须的才能使它正常工作。@DSM出于某种原因,我认为这是pandas早期版本的默认行为。我现在不得不重写以前执行相同操作的代码,比如 a - a['2005'] - pbreach

3

如果要计算相对于开始时间经过了多长时间,可以使用时间戳值:

df['Time_column'].apply(lambda x: x-df.iloc[[0],[1]])

df.iloc[[0],[1]] 表示开始时间。


1
谢谢 - 除非你也进行赋值,因为.apply不是原地操作,所以 df['Time_column'] = df['Time_column'].apply... - sdbbs

3

以下是如何完成此操作的更详细说明。

首先,创建一个简单的DataFrame以便更容易理解。

import numpy as np
import pandas as pd
#make a simple DataFrame
df = pd.DataFrame(np.fromfunction(lambda i, j: i+1 , (3, 3), dtype=int))

这将会看起来像这样。
# 1 1 1
# 2 2 2
# 3 3 3

现在从第一行获取数值

first_row = df.iloc[[0]].values[0]

现在使用apply()函数将第一行从其余的行中减去。
df.apply(lambda row: row - first_row, axis=1)

结果将会是这样。注意每一行都减去了1。
#  0 0 0
#  1 1 1
#  2 2 2

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