【问题标题】:Conditional column selection in pandas熊猫中的条件列选择
【发布时间】:2016-11-06 03:16:51
【问题描述】:

我想根据特定条件从 DataFrame 中选择列。我知道可以通过循环来完成,但我的 df 非常大,因此效率至关重要。列选择的条件是只有非 nan 条目或只有 nan 的序列后跟只有非 nan 条目的序列。

这是一个例子。考虑以下 DataFrame:

pd.DataFrame([[1, np.nan, 2, np.nan], [2, np.nan, 5, np.nan], [4, 8, np.nan, 1], [3, 2, np.nan, 2], [3, 2, 5, np.nan]])

   0    1    2    3
0  1  NaN  2.0  NaN
1  2  NaN  5.0  NaN
2  4  8.0  NaN  1.0
3  3  2.0  NaN  2.0
4  3  2.0  5.0  NaN

从中,我只想选择第 0 列和第 1 列。关于如何在不循环的情况下有效地执行此操作有什么建议吗?

【问题讨论】:

    标签: python-3.x pandas dataframe


    【解决方案1】:

    逻辑

    • 计算每列中的空值。如果唯一的空值位于开头,则列中的空值数应等于第一个有效索引的位置。
    • 获取第一个有效索引
    • 按空计数对索引进行切片并与第一个有效索引进行比较。如果它们相等,那么这是一个很好的列

    cnull = df.isnull().sum()
    fvald = df.apply(pd.Series.first_valid_index)
    cols = df.index[cnull] == fvald
    df.loc[:, cols]
    


    编辑速度提升

    旧答案

    def pir1(df):
        cnull = df.isnull().sum()
        fvald = df.apply(pd.Series.first_valid_index)
        cols = df.index[cnull] == fvald
        return df.loc[:, cols]
    

    使用相同的逻辑更快地回答

    def pir2(df):
        nulls = np.isnan(df.values)
        null_count = nulls.sum(0)
        first_valid = nulls.argmin(0)
        null_on_top = null_count == first_valid
        filtered_data = df.values[:, null_on_top]
        filtered_columns = df.columns.values[null_on_top]
        return pd.DataFrame(filtered_data, df.index, filtered_columns)
    

    【讨论】:

    • 感谢@piRSquared。该解决方案确实可以完成工作,但运行时间比下面发布的解决方案长 3 倍以上
    • @splinter 我并不感到惊讶。我想过走 Nickil 的路线,但我选择了简洁。 Nickil 提供了一个很好的答案。我将使用相同的逻辑更新我的帖子,但使用一些技巧来加快速度。
    • 听起来很棒@piRSquared
    • 你说得对,它要快得多。就我而言,它比 NickiMaveli 提出的解决方案快 4 倍
    【解决方案2】:

    考虑一个DF,如图所示,它在各种可能的位置都有Nans

    1. 双方Nans出席

    通过将所有 nan 替换为 0 并将有限值替换为 1 来创建掩码:

    mask = np.where(np.isnan(df), 0, 1)
    

    取每列对应的元素差异。接下来,取其值的模数。这里的逻辑是,只要每列中存在三个唯一值,则丢弃该列(即 → -1,1,0),因为这种情况会导致序列中断。

    想法是取总和并在总和小于 2 的任何地方创建一个子集。(取 mod 后,我们得到 1,1,0)。因此,对于极端情况,我们得到 sum 为 2,这些列肯定是不相交的,必须丢弃。

    criteria = pd.DataFrame(mask, columns=df.columns).diff(1).abs().sum().lt(2)
    

    最后转置DF 并使用此条件并重新转置以获得所需的结果,其中一部分只有Nans,另一部分只有有限值。

    df.loc[:, criteria]
    

    2. Nans 在顶部:

    mask = np.where(np.isnan(df), 0, 1)
    criteria = pd.DataFrame(mask, columns=df.columns).diff(1).ne(-1).any()
    df.loc[:, criteria]
    

    【讨论】:

    • 工作得很好@NickiMaveli,它的速度是上述解决方案的 3 倍。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-16
    • 1970-01-01
    • 2020-04-27
    • 1970-01-01
    • 2019-06-16
    • 1970-01-01
    • 2023-03-19
    相关资源
    最近更新 更多