【问题标题】:How do I find: Is the first non-NaN value in each column the maximum for that column in a DataFrame?如何找到:每列中的第一个非 NaN 值是否是 DataFrame 中该列的最大值?
【发布时间】:2019-02-20 07:14:54
【问题描述】:

例如:

      0     1
0  87.0   NaN
1   NaN  99.0
2   NaN   NaN
3   NaN   NaN
4   NaN  66.0
5   NaN   NaN
6   NaN  77.0
7   NaN   NaN
8   NaN   NaN
9  88.0   NaN

我的预期输出是:[False, True],因为 87 是第一个 !NaN 值,但不是 0 列中的最大值。 99 但是是第一个 !NaN 值,并且确实是该列中的最大值。

【问题讨论】:

标签: python pandas max nan


【解决方案1】:

选项 a):只需使用 groupbyfirst

(可能不是 100% reliable

df.groupby([1]*len(df)).first()==df.max()
Out[89]: 
       0     1
1  False  True

选项 b)bfill

或者使用bfill(任意NaN值用列中的后向值填充,那么bfill之后的第一行就是第一个不是NaN的值)

df.bfill().iloc[0]==df.max()
Out[94]: 
0    False
1     True
dtype: bool

选项 c)stack

df.stack().reset_index(level=1).drop_duplicates('level_1').set_index('level_1')[0]==df.max()
Out[102]: 
level_1
0    False
1     True
dtype: bool

选项 d)idxmaxfirst_valid_index

df.idxmax()==df.apply(pd.Series.first_valid_index)
Out[105]: 
0    False
1     True
dtype: bool

选项 e)(来自 Pir)idxmaxisna

df.notna().idxmax() == df.idxmax()     
Out[107]: 
0    False
1     True
dtype: bool

【讨论】:

  • 目前,groupby/first 返回每​​个组的第一个非 NaN 值。但我不确定我们是否应该依赖它,因为首席开发人员似乎是possibly consider this a bug
  • opt 4 非常好,你。
  • 我没有看到 df.notna().idxmax() == df.idxmax()
【解决方案2】:

发布问题后,我想出了这个问题:

def nice_method_name_here(sr):
    return sr[sr > 0][0] == np.max(sr)

print(df.apply(nice_method_name_here))

这似乎有效,但还不确定!

【讨论】:

    【解决方案3】:

    您可以使用底层 Numpy 数组执行类似于 Wens 的回答:

    >>> df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0).values
    array([False,  True])
    

    df.max(axis=0) 给出按列的最大值。

    左侧索引df.values,它是一个二维数组,使其成为一维数组并将其逐个元素地与每列的最大值进行比较。

    如果您从右侧排除 .values,则结果将只是 Pandas 系列:

    >>> df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0)
    0    False
    1     True
    dtype: bool
    

    【讨论】:

      【解决方案4】:

      使用纯numpy(我觉得这样很快)

      >>> np.isnan(df.values).argmin(axis=0) == df.fillna(-np.inf).values.argmax(axis=0)
      array([False,  True])
      

      想法是比较第一个非nan的索引是否也是argmax的索引。

      时间

      df = pd.concat([df]*1000).reset_index(drop=True) # setup
      
      %timeit np.isnan(df.values).argmin(axis=0) == df.fillna(-np.inf).values.argmax(axis=0)
      207 µs ± 8.83 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      
      %timeit df.groupby([1]*len(df)).first()==df.max()
      9.78 ms ± 339 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      
      %timeit df.bfill().iloc[0]==df.max()
      824 µs ± 47.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      
      %timeit df.stack().reset_index(level=1).drop_duplicates('level_1').set_index('level_1')[0]==df.max()
      3.55 ms ± 249 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      
      %timeit df.idxmax()==df.apply(pd.Series.first_valid_index)
      1.5 ms ± 25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      
      %timeit df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0)
      1.13 ms ± 14.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      
      %timeit df.values[(~np.isnan(df.values)).argmax(axis=0), np.arange(df.shape[1])] == df.max(axis=0).values
      450 µs ± 20.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      

      【讨论】:

      • 如果列中的所有值都是 NaN,这会起作用吗?这种情况下的预期行为是False
      【解决方案5】:

      我们可以在这里使用numpynanmax 以获得有效的解决方案:

      a = df.values
      np.nanmax(a, 0) == a[np.isnan(a).argmin(0), np.arange(a.shape[1])]
      

      array([False,  True])
      

      时间安排(这里提供了很多选项):


      函数

      def chris(df):
          a = df.values
          return np.nanmax(a, 0) == a[np.isnan(a).argmin(0), np.arange(a.shape[1])]
      
      def bradsolomon(df):
          df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0).values
      
      def wen1(df):
          return df.groupby([1]*len(df)).first()==df.max()
      
      def wen2(df):
          return df.bfill().iloc[0]==df.max()
      
      def wen3(df):
          return df.idxmax()==df.apply(pd.Series.first_valid_index)
      
      def rafaelc(df):
          return np.isnan(df.values).argmin(axis=0) == df.fillna(-np.inf).values.argmax(axis=0)
      
      def pir(df):
          return df.notna().idxmax() == df.idxmax()
      

      设置

      res = pd.DataFrame(
             index=['chris', 'bradsolomon', 'wen1', 'wen2', 'wen3', 'rafaelc', 'pir'],
             columns=[10, 20, 30, 100, 500, 1000],
             dtype=float
      )
      
      for f in res.index:
          for c in res.columns:
              a = np.random.rand(c, c)
              a[a > 0.4] = np.nan
              df = pd.DataFrame(a)
              stmt = '{}(df)'.format(f)
              setp = 'from __main__ import df, {}'.format(f)
              res.at[f, c] = timeit(stmt, setp, number=50)
      
      ax = res.div(res.min()).T.plot(loglog=True)
      ax.set_xlabel("N");
      ax.set_ylabel("time (relative)");
      
      plt.show()
      

      结果

      【讨论】:

        猜你喜欢
        • 2022-12-03
        • 2019-12-09
        • 2022-08-18
        • 1970-01-01
        • 1970-01-01
        • 2021-02-03
        • 2018-04-26
        • 2023-03-17
        • 1970-01-01
        相关资源
        最近更新 更多