求和 vs np.nansum 在 pandas 数据帧上对具有相同名称的列求和时的怪异 - python
sum vs np.nansum weirdness while summing columns with same name on a pandas dataframe - python
从这里关于 SO (Merge Columns within a DataFrame that have the Same Name) 的讨论中获得灵感,我尝试了建议的方法,虽然它在使用函数 sum()
时有效,但在我使用 np.nansum
:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(100,4), columns=['a', 'a','b','b'], index=pd.date_range('2011-1-1', periods=100))
print(df.head(3))
sum()
案例:
print(df.groupby(df.columns, axis=1).apply(sum, axis=1).head(3))
a b
2011-01-01 1.328933 1.678469
2011-01-02 1.878389 1.343327
2011-01-03 0.964278 1.302857
np.nansum()
案例:
print(df.groupby(df.columns, axis=1).apply(np.nansum, axis=1).head(3))
a [1.32893299939, 1.87838886222, 0.964278430632,...
b [1.67846885234, 1.34332662587, 1.30285727348, ...
dtype: object
知道为什么吗?
问题在于 np.nansum
将其输入转换为 numpy 数组,因此它实际上丢失了列信息(sum
不会这样做)。因此,groupby
在构造输出时不会返回任何列信息,因此输出只是一系列 numpy 数组。
具体来说,the source code for np.nansum
calls the _replace_nan
function. In turn, the source code for _replace_nan
检查输入是否为数组,如果不是,则将其转换为 1。
虽然所有的希望都没有消失。您可以使用 Pandas 函数轻松复制 np.nansum
。具体使用 sum
后跟 fillna
:
df.groupby(df.columns, axis=1).sum().fillna(0)
sum
应该忽略 NaN
并且只对非空值求和。您会得到 NaN
的唯一情况是所有试图求和的值都是 NaN
,这就是为什么需要 fillna
的原因。请注意,您也可以在 groupby
之前执行 fillna
,即 df.fillna(0).groupby...
.
如果真的要用np.nansum
,可以重铸为pd.Series
。这可能会影响性能,因为构建一个 Series 可能相对昂贵,而且您会多次这样做:
df.groupby(df.columns, axis=1).apply(lambda x: pd.Series(np.nansum(x, axis=1), x.index))
示例计算
对于一些示例计算,我将使用以下简单的 DataFrame,其中包含 NaN
个值(您的示例数据不包含):
df = pd.DataFrame([[1,2,2,np.nan,4],[np.nan,np.nan,np.nan,3,3],[np.nan,np.nan,-1,2,np.nan]], columns=list('aaabb'))
a a a b b
0 1.0 2.0 2.0 NaN 4.0
1 NaN NaN NaN 3.0 3.0
2 NaN NaN -1.0 2.0 NaN
使用 sum
不使用 fillna
:
df.groupby(df.columns, axis=1).sum()
a b
0 5.0 4.0
1 NaN 6.0
2 -1.0 2.0
使用 sum
和 fillna
:
df.groupby(df.columns, axis=1).sum().fillna(0)
a b
0 5.0 4.0
1 0.0 6.0
2 -1.0 2.0
与固定的np.nansum
方法相比:
df.groupby(df.columns, axis=1).apply(lambda x: pd.Series(np.nansum(x, axis=1), x.index))
a b
0 5.0 4.0
1 0.0 6.0
2 -1.0 2.0
从这里关于 SO (Merge Columns within a DataFrame that have the Same Name) 的讨论中获得灵感,我尝试了建议的方法,虽然它在使用函数 sum()
时有效,但在我使用 np.nansum
:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(100,4), columns=['a', 'a','b','b'], index=pd.date_range('2011-1-1', periods=100))
print(df.head(3))
sum()
案例:
print(df.groupby(df.columns, axis=1).apply(sum, axis=1).head(3))
a b
2011-01-01 1.328933 1.678469
2011-01-02 1.878389 1.343327
2011-01-03 0.964278 1.302857
np.nansum()
案例:
print(df.groupby(df.columns, axis=1).apply(np.nansum, axis=1).head(3))
a [1.32893299939, 1.87838886222, 0.964278430632,...
b [1.67846885234, 1.34332662587, 1.30285727348, ...
dtype: object
知道为什么吗?
问题在于 np.nansum
将其输入转换为 numpy 数组,因此它实际上丢失了列信息(sum
不会这样做)。因此,groupby
在构造输出时不会返回任何列信息,因此输出只是一系列 numpy 数组。
具体来说,the source code for np.nansum
calls the _replace_nan
function. In turn, the source code for _replace_nan
检查输入是否为数组,如果不是,则将其转换为 1。
虽然所有的希望都没有消失。您可以使用 Pandas 函数轻松复制 np.nansum
。具体使用 sum
后跟 fillna
:
df.groupby(df.columns, axis=1).sum().fillna(0)
sum
应该忽略 NaN
并且只对非空值求和。您会得到 NaN
的唯一情况是所有试图求和的值都是 NaN
,这就是为什么需要 fillna
的原因。请注意,您也可以在 groupby
之前执行 fillna
,即 df.fillna(0).groupby...
.
如果真的要用np.nansum
,可以重铸为pd.Series
。这可能会影响性能,因为构建一个 Series 可能相对昂贵,而且您会多次这样做:
df.groupby(df.columns, axis=1).apply(lambda x: pd.Series(np.nansum(x, axis=1), x.index))
示例计算
对于一些示例计算,我将使用以下简单的 DataFrame,其中包含 NaN
个值(您的示例数据不包含):
df = pd.DataFrame([[1,2,2,np.nan,4],[np.nan,np.nan,np.nan,3,3],[np.nan,np.nan,-1,2,np.nan]], columns=list('aaabb'))
a a a b b
0 1.0 2.0 2.0 NaN 4.0
1 NaN NaN NaN 3.0 3.0
2 NaN NaN -1.0 2.0 NaN
使用 sum
不使用 fillna
:
df.groupby(df.columns, axis=1).sum()
a b
0 5.0 4.0
1 NaN 6.0
2 -1.0 2.0
使用 sum
和 fillna
:
df.groupby(df.columns, axis=1).sum().fillna(0)
a b
0 5.0 4.0
1 0.0 6.0
2 -1.0 2.0
与固定的np.nansum
方法相比:
df.groupby(df.columns, axis=1).apply(lambda x: pd.Series(np.nansum(x, axis=1), x.index))
a b
0 5.0 4.0
1 0.0 6.0
2 -1.0 2.0