版本 0.22.0 (2017年12月29日)#

这是从 0.21.1 版本以来的一个主要发布,并且包含一个 API 破坏性的更改。我们建议所有用户在仔细阅读发布说明(单数!)后升级到这个版本。

向后不兼容的 API 变化#

pandas 0.22.0 改变了对空值和所有-NA 的和与积的处理方式。简而言之是

  • 一个空的或全部为 NASeries 的总和现在是 0

  • 一个空的或全部为 NASeries 的乘积现在是 1

  • 我们为 .sum().prod() 添加了一个 min_count 参数,用于控制结果有效的有效值的最小数量。如果存在的非*NA*值少于 min_count ,则结果为*NA*。默认值是 0 。要返回 NaN ,即0.21版本的行为,请使用 min_count=1

一些背景:在 pandas 0.21 中,我们修复了一个长期存在的,根据是否安装了 bottleneck 而返回值不一致的所有-NA 系列的错误。请参见 所有-NaN 或空 Series/DataFrames 的总和/乘积现在一致为 NaN。同时,我们将空 Series 的 sum 和 prod 也改为 NaN

基于反馈,我们部分恢复了这些更改。

算术运算#

默认情况下,空或全为 NASeries 的总和现在是 0

pandas 0.21.x

In [1]: pd.Series([]).sum()
Out[1]: nan

In [2]: pd.Series([np.nan]).sum()
Out[2]: nan

pandas 0.22.0

In [1]: pd.Series([]).sum()
Out[1]: 0

In [2]: pd.Series([np.nan]).sum()
Out[2]: 0.0

默认行为与安装了 bottleneck 的 pandas 0.20.3 相同。它还与 NumPy 的 np.nansum 在空数组和所有-NA 数组上的行为相匹配。

要让空系列的和返回 NaN``(这是没有 bottleneck pandas 0.20.3 pandas 0.21.x 的默认行为),请使用 ``min_count 关键字。

In [3]: pd.Series([]).sum(min_count=1)
Out[3]: nan

由于 skipna 参数,所有*NA* 系列的 .sum 在概念上与 skipna=True``(默认值)的空系列的 ``.sum 相同。

In [4]: pd.Series([np.nan]).sum(min_count=1)  # skipna=True by default
Out[4]: nan

min_count 参数指的是对非NA求和或乘积所需的最小 非空 值数量。

Series.prod() 已被更新,其行为与 Series.sum() 相同,返回 1

In [5]: pd.Series([]).prod()
Out[5]: 1

In [6]: pd.Series([np.nan]).prod()
Out[6]: 1.0

In [7]: pd.Series([]).prod(min_count=1)
Out[7]: nan

这些更改也影响 DataFrame.sum()DataFrame.prod()。最后,pandas 中一些不太明显的地方也受到此更改的影响。

按分类分组#

Categorical 分组并求和现在返回 0 而不是 NaN 对于没有观察值的类别。乘积现在返回 1 而不是 NaN

pandas 0.21.x

In [8]: grouper = pd.Categorical(['a', 'a'], categories=['a', 'b'])

In [9]: pd.Series([1, 2]).groupby(grouper, observed=False).sum()
Out[9]:
a    3.0
b    NaN
dtype: float64

pandas 0.22

In [8]: grouper = pd.Categorical(["a", "a"], categories=["a", "b"])

In [9]: pd.Series([1, 2]).groupby(grouper, observed=False).sum()
Out[9]: 
a    3
b    0
Length: 2, dtype: int64

要恢复 0.21 版本中对未观测组的 NaN 返回行为,请使用 min_count>=1

In [10]: pd.Series([1, 2]).groupby(grouper, observed=False).sum(min_count=1)
Out[10]: 
a    3.0
b    NaN
Length: 2, dtype: float64

重采样#

所有-NA 区间的总和和乘积已从 NaN 变为总和为 0 和乘积为 1

pandas 0.21.x

In [11]: s = pd.Series([1, 1, np.nan, np.nan],
   ....:               index=pd.date_range('2017', periods=4))
   ....: s
Out[11]:
2017-01-01    1.0
2017-01-02    1.0
2017-01-03    NaN
2017-01-04    NaN
Freq: D, dtype: float64

In [12]: s.resample('2d').sum()
Out[12]:
2017-01-01    2.0
2017-01-03    NaN
Freq: 2D, dtype: float64

pandas 0.22.0

In [11]: s = pd.Series([1, 1, np.nan, np.nan],
   ....:               index=pd.date_range("2017", periods=4))

In [12]: s.resample("2d").sum()
Out[12]:
2017-01-01    2.0
2017-01-03    0.0
Freq: 2D, Length: 2, dtype: float64

要恢复 0.21 版本返回 NaN 的行为,请使用 min_count>=1

In [13]: s.resample("2d").sum(min_count=1)
Out[13]:
2017-01-01    2.0
2017-01-03    NaN
Freq: 2D, Length: 2, dtype: float64

特别是,上采样和求和或求积会受到影响,因为即使原始序列完全有效,上采样也会引入缺失值。

pandas 0.21.x

In [14]: idx = pd.DatetimeIndex(['2017-01-01', '2017-01-02'])

In [15]: pd.Series([1, 2], index=idx).resample('12H').sum()
Out[15]:
2017-01-01 00:00:00    1.0
2017-01-01 12:00:00    NaN
2017-01-02 00:00:00    2.0
Freq: 12H, dtype: float64

pandas 0.22.0

In [14]: idx = pd.DatetimeIndex(["2017-01-01", "2017-01-02"])
In [15]: pd.Series([1, 2], index=idx).resample("12H").sum()
Out[15]:
2017-01-01 00:00:00    1
2017-01-01 12:00:00    0
2017-01-02 00:00:00    2
Freq: 12H, Length: 3, dtype: int64

再次强调,min_count 关键字可用于恢复 0.21 版本的行为。

In [16]: pd.Series([1, 2], index=idx).resample("12H").sum(min_count=1)
Out[16]:
2017-01-01 00:00:00    1.0
2017-01-01 12:00:00    NaN
2017-01-02 00:00:00    2.0
Freq: 12H, Length: 3, dtype: float64

滚动和扩展#

滚动和扩展已经有一个 min_periods 关键字,其行为类似于 min_count。唯一改变的情况是在使用 min_periods=0 进行滚动或扩展求和时。以前这会返回 NaN,当窗口中少于 min_periods 个非*NA* 值时。现在它返回 0

pandas 0.21.1

In [17]: s = pd.Series([np.nan, np.nan])

In [18]: s.rolling(2, min_periods=0).sum()
Out[18]:
0   NaN
1   NaN
dtype: float64

pandas 0.22.0

In [11]: s = pd.Series([np.nan, np.nan])

In [12]: s.rolling(2, min_periods=0).sum()
Out[12]: 
0    0.0
1    0.0
Length: 2, dtype: float64

min_periods=None 的默认行为保持不变,即 min_periods 等于窗口大小。

兼容性#

如果你维护一个应该在不同 pandas 版本中工作的库,最简单的方法可能是从你的要求中排除 pandas 0.21。否则,你所有的 sum() 调用都需要在求和之前检查 Series 是否为空。

使用 setuptools,在你的 setup.py 中使用:

install_requires=['pandas!=0.21.*', ...]

使用 conda,请

requirements:
  run:
    - pandas !=0.21.0,!=0.21.1

请注意,对于所有-NA 系列的返回值不一致性在 pandas 0.20.3 及更早版本中仍然存在。避免使用 pandas 0.21 只能帮助处理空的情况。

贡献者#

总共有 1 个人为这次发布贡献了补丁。名字旁边有 “+” 的人首次贡献了补丁。

  • Tom Augspurger