【问题标题】:Rowwise min() and max() fails for column with NaNs对于具有 NaN 的列,按行 min() 和 max() 失败
【发布时间】:2018-02-04 13:30:42
【问题描述】:

我正在尝试获取包含日期的两列的行最大值(和最小值)

from datetime import date
import pandas as pd
import numpy as np    

df = pd.DataFrame({'date_a' : [date(2015, 1, 1), date(2012, 6, 1),
                               date(2013, 1, 1), date(2016, 6, 1)],
                   'date_b' : [date(2012, 7, 1), date(2013, 1, 1), 
                               date(2014, 3, 1), date(2013, 4, 1)]})

df[['date_a', 'date_b']].max(axis=1)
Out[46]: 
0    2015-01-01
1    2013-01-01
2    2014-03-01
3    2016-06-01

正如预期的那样。但是,如果数据帧包含单个 NaN 值,则整个操作将失败

df_nan = pd.DataFrame({'date_a' : [date(2015, 1, 1), date(2012, 6, 1),
                                   np.NaN, date(2016, 6, 1)],
                       'date_b' : [date(2012, 7, 1), date(2013, 1, 1), 
                                   date(2014, 3, 1), date(2013, 4, 1)]})

df_nan[['date_a', 'date_b']].max(axis=1)
Out[49]: 
0   NaN 
1   NaN
2   NaN
3   NaN
dtype: float64

这里发生了什么?我期待这个结果

0    2015-01-01
1    2013-01-01
2    NaN
3    2016-06-01

如何做到这一点?

【问题讨论】:

  • 问题似乎不在于 NaN 是 NaN;任何浮动似乎都会导致同样的问题。我认为浮动触发了所有元素到浮动的转换。
  • 你为什么不使用pandas datetime` dtype 而不是object dtype?
  • FWIW,我不认为这是一个错误。默认情况下,numeric_only 参数设置为 None ,其值由(混合)dtypes 确定。 DataFrame 中的单个浮点数将 numeric_only 标志设置为 True 并且由于您没有任何数字列,因此您的结果都是 NaN。 pd.DataFrame({'A': ['a', 'b', 'c']}).max(axis=1, numeric_only=True) 也返回所有的 NaN。并且df_nan.max(axis=1, numeric_only=False) 引发了一个 TypeError 应该做的事情。

标签: python pandas date datetime nan


【解决方案1】:

date 对象与列中的浮点数(例如NaN)混合时,似乎会发生这种情况。默认情况下,numeric_only 标志设置是因为单个浮点值。例如,将您的 df_nan 替换为:

df_float = pd.DataFrame({'date_a' : [date(2015, 1, 1), date(2012, 6, 1),
                                    1.023, date(2016, 6, 1)],
                        'date_b' : [date(2012, 7, 1), 3.14, 
                                    date(2014, 3, 1), date(2013, 4, 1)]})

print(df_float.max(1))

0   NaN
1   NaN
2   NaN
3   NaN
dtype: float64

如果手动将该标志设置为 false,这将正确地抛出 TypeError,因为:

print(date(2015, 1, 1) < 1.0)

TypeError                                 Traceback (most recent call last)
<ipython-input-362-ccbf44ddb40a> in <module>()
      1 
----> 2 print(date(2015, 1, 1) < 1.0)

TypeError: unorderable types: datetime.date() < float()

但是,pandas 似乎将所有内容都强制为NaN。作为一种解决方法,使用df.astype 转换为str 似乎可以做到这一点:

out = df_nan.astype(str).max(1)
print(out) 
0    2015-01-01
1    2013-01-01
2           nan
3    2016-06-01
dtype: object

在这种情况下,按字典顺序排序会产生与以前相同的解决方案。

否则,作为juan suggests,您可以使用pd.to_datetime 转换为datetime

out = df_nan.apply(pd.to_datetime, errors='coerce').max(1)
print(out)

0   2015-01-01
1   2013-01-01
2   2014-03-01
3   2016-06-01
dtype: datetime64[ns]

【讨论】:

    【解决方案2】:

    以下应该有效:

    >>> df_nan.where(df_nan.T.notnull().all()).max(axis=1)
    Out[1]:
    0    2015-01-01
    1    2013-01-01
    2          None
    3    2016-06-01
    dtype: object
    

    地点:

    1. df_nan.T.notnull().all() 计算不包含 np.nan 的行掩码
    2. df_nan.where() 将前一个掩码应用于数据帧
    3. .max(axis=1) 获取逐行最大值

    之所以有效,是因为所有值为np.nan 的数组的最大值为None。它允许通过不显示最大值来跟踪缺少值的行。

    但是这个决定取决于你,否则@juanpa.arrivillaga 将NaN 转换为NaT 的解决方案就是你想要的。

    【讨论】:

      【解决方案3】:

      我想说最好的解决方案是使用适当的dtype。 Pandas 提供了一个很好集成的datetimedtype。所以请注意,您使用的是object dtypes...

      >>> df
             date_a      date_b
      0  2015-01-01  2012-07-01
      1  2012-06-01  2013-01-01
      2         NaN  2014-03-01
      3  2016-06-01  2013-04-01
      >>> df.dtypes
      date_a    object
      date_b    object
      dtype: object
      

      但请注意,当您使用时问题消失了

      >>> df2 = df.apply(pd.to_datetime)
      >>> df2
            date_a     date_b
      0 2015-01-01 2012-07-01
      1 2012-06-01 2013-01-01
      2        NaT 2014-03-01
      3 2016-06-01 2013-04-01
      >>> df2.min(axis=1)
      0   2012-07-01
      1   2012-06-01
      2   2014-03-01
      3   2013-04-01
      dtype: datetime64[ns]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-24
        • 2018-06-04
        • 2021-09-11
        • 2018-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-22
        相关资源
        最近更新 更多