Pandas:获取最少的记录数,以便所有列至少有一个非null值

时间:2018-04-19 16:22:55

标签: python pandas dataframe

我有一个62列的数据框,大部分都是空的。某些记录具有多个具有非空值的列,而其他记录只有一个非空值。我想知道是否有办法使用.dropna或其他策略返回最少行数,每列至少有一个非空值。

简化示例

       a          b         c
      NaN        1         NaN
      1          NaN       NaN
      NaN        NaN       NaN
      NaN        1         1

会返回

      a          b         c
      1          NaN       NaN
      NaN        1         1

...

2 个答案:

答案 0 :(得分:5)

这是一个简单的贪婪解决方案,可以完成这项工作,但不能保证你拥有最少的行数(因为@chthonicdaemon说问题是NP难的)

import pandas as pd
import numpy as np

# sample dataframe
df = pd.DataFrame({'a':[np.nan,1,np.nan,np.nan],'b':[1, np.nan, np.nan, 1],'c':[np.nan, np.nan, np.nan, 1]})
df_orig = df
cols = df.columns.tolist()

rows = []
while not df.empty:
    ## Find the row with most non-null column entries
    x = df.notnull().sum(axis=1).idxmax() # edit - fix for null/nonnull
    ## Add the row to our list and continue
    rows.append(x)
    ## Remove the columns from our dataframe
    df = df.drop(columns=df.columns[df.loc[[x]].notnull().any()].tolist())

## Access the dataframe with only 'essential' rows
df_orig.loc[rows]

输出:

    a   b   c
3   NaN 1.0 1.0
1   1.0 NaN NaN

答案 1 :(得分:1)

我创建了一个对NaN进行大量过滤的函数,但它也不保证最小行数。实际上,Brendan Frick answer总是返回较少的行。这个更注重性能。

这个想法基本上是计算非空值的数量,并对数据帧进行切片,使其达到计数阈值。如果切片的数据帧满足至少一个非空值的条件,则返回,否则,修改阈值。

def custom_dropna(df):
    counts=df.count(axis=1)
    for i in sorted(set(counts.values),reverse=True):
        dropped_df = df[counts>=i]
        if dropped_df.count().min()>=1:
            return dropped_df

绩效分析

为了检查函数是否有效以及它是如何工作的,我生成了一个随机输入数据帧,并为df的不同属性执行了函数。

df = pd.DataFrame(np.random.choice([np.nan,1],size=(500,62),p=[.2,.8]),columns=range(62))

P(null)= 0.2,行= 500

custom_dropna(df).count().values
# returned a dataframe with 3 rows
# [3 3 3 3 2 3 3 3 3 3 3 3 3 2 3 3 3 3 3 2 3 3 3 3 3 3 2 3 3 3 3 2 3 3 3 2 3
# 3 3 3 3 3 3 3 1 3 3 3 3 2 2 2 3 3 2 3 3 1 3 2 3 3]
# 631 µs ± 9.53 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
answer_Brendan_Frick(df).count().values
# returned 2 rows
# [1 2 2 2 1 1 1 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 1 2 2 2 2 1 1 1 1 2 2 2
# 2 2 1 1 2 2 2 1 1 1 2 2 1 2 1 1 2 2 2 2 2 1 2 2 1]
# 3 ms ± 8.19 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

我重复了5次时间和代码执行。 custom_dropna返回2到5行,而answer_Brendan_Frick总是返回2。每次重复的执行时间基本相同,分别为.6-.7 ms和2.9-3.1 ms。

P(null)= 0.8,行= 500

custom_dropna(df).count().values
# returned 12 rows
# [2 4 5 4 4 3 5 3 4 3 2 8 6 5 4 2 5 4 6 8 6 2 5 5 6 4 3 1 4 4 4 4 6 7 4 3 4
# 5 3 4 3 1 4 3 1 6 3 2 6 6 4 4 4 5 5 5 3 4 4 6 5 4]
# 1.96 ms ± 34.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
answer_Brendan_Frick(df).count().values
# returned 6 rows
# [1 2 2 1 2 1 1 3 1 1 1 2 3 1 1 1 2 2 2 3 3 2 2 2 1 2 2 1 1 2 2 1 1 1 3 1 2
# 1 1 1 4 2 2 1 2 2 1 1 1 2 2 2 1 2 2 1 2 2 1 1 1 2]
# 8.21 ms ± 148 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

我重复了5次时间和代码执行。 custom_dropna返回7到13行,而answer_Brendan_Frick总是返回6。每次重复的执行时间基本相同,分别为1.5-2 ms和8.1-8.3 ms。

P(null)= 0.8,行= 5000

在这种情况下,custom_dropna返回7到23行,而answer_Brendan_Frick总是返回5。每次重复的执行时间基本相同,分别为2.4-2.7 ms和14.3-14.5 ms。

custom_dropna返回的大量行是由于许多行具有相同的计数值,对于大型数据帧来说这将变得更糟。