【问题标题】:Using pandas to fill gaps only, and not NaNs on the ends仅使用 pandas 来填补空白,而不是在末端使用 NaN
【发布时间】:2021-07-20 20:36:29
【问题描述】:

我有一些跨越大约 8 个月的房价数据,并跟踪房价从房屋上市到售出的过程。我想填补中间数据中的几个空白,但我想将 NaN 保留在每个空白的末尾。

举个简单的例子,假设我们有房子 1,它在“第 4 天”以 200000 的价格上市,在“第 9 天”以 190000 的价格出售。我们有 house2 在第 1 天到第 12 天保持在 180000 并且在那个时间窗口内不出售。但是,第 6 天和第 7 天出了点问题,我丢失了数据:

house1 = [NaN, NaN, NaN, 200000, 200000, NaN, NaN, 200000, 190000, NaN, NaN, NaN]
house2 = [180000, 180000, 180000, 180000, 180000, NaN, NaN, 180000, 180000, 180000, 180000, 180000]

现在想象一下,这些是 Pandas Dataframes 中按日期索引的列,而不是常规数组。

问题是,我通常用来填补这里空白的函数是DataFrame.fillna(),使用 backfill 或 ffill 方法。如果我使用 ffill,house1 会返回:

house1 = [NaN, NaN, NaN, 200000, 200000, 200000, 200000, 200000, 190000, 190000, 190000, 190000]

这填补了空白,但也错误地填写了销售日之后的数据。如果我改用回填,我会得到:

house1 = [200000, 200000, 200000, 200000, 200000, 200000, 200000, 200000, 190000, NaN, NaN, NaN]

再次填补了空白,但这次也填补了数据的前端。如果我将 'limit=2' 与 ffill 一起使用,那么我得到的是:

house1 = [NaN, NaN, NaN, 200000, 200000, 200000, 200000, 200000, 190000, 190000, 190000, NaN]

它再次填补了空白,但随后它也开始填补“真实”数据结束位置之外的数据。

到目前为止,我的解决方案是编写以下函数:

def fillGaps(houseDF):
    """Fills up holes in the housing data"""

    def fillColumns(column):
        filled_col = column
        lastValue = None
        # Keeps track of if we are dealing with a gap in numbers
        gap = False
        i = 0
        for currentValue in filled_col:
            # Loops over all the nans before the numbers begin
            if not isANumber(currentValue) and lastValue is None:
                pass
            # Keeps track of the last number we encountered before a gap
            elif isANumber(currentValue) and (gap is False):
                lastIndex = i
                lastValue = currentValue
            # Notes when we encounter a gap in numbers
            elif not isANumber(currentValue):
                gap = True
            # Fills in the gap
            elif isANumber(currentValue):
                gapIndicies = range(lastIndex + 1, i)
                for j in gapIndicies:
                    filled_col[j] = lastValue
                gap = False
            i += 1
        return filled_col

    filled_df = houseDF.apply(fillColumns, axis=0)
    return filled_df

它只是跳过前面的所有 NaN,填充间隙(由实际值之间的 NaN 组定义),最后不填充 NaN。

有没有更简洁的方法,或者我不知道的内置 pandas 函数?

【问题讨论】:

    标签: python numpy pandas


    【解决方案1】:

    一年后我找到了这个答案,但需要它在具有多列的 DataFrame 上工作,所以我想把我的解决方案留在这里,以防其他人需要同样的解决方案。我的函数只是YS-L的修改版

    def fillna_downbet(df):
        df = df.copy()
        for col in df:
            non_nans = df[col][~df[col].apply(np.isnan)]
            start, end = non_nans.index[0], non_nans.index[-1]
            df[col].loc[start:end] = df[col].loc[start:end].fillna(method='ffill')
        return df
    

    谢谢!

    【讨论】:

      【解决方案2】:

      另一种解决多列 DataFrame 的方法

      df.fillna(method='ffill') + (df.fillna(method='bfill') * 0)
      

      它是如何工作的?

      第一个fillna 会向前填充值。这几乎是我们想要的,只是它在每个系列的末尾留下了填充值的痕迹。

      第二个fillna 向后填充我们乘以零的值。结果是我们不需要的尾随值将是 NaN,而其他所有值都将为 0。

      最后,我们利用 x + 0 = x 和 x + NaN = NaN 的事实将两者相加。

      【讨论】:

        【解决方案3】:

        您可以在系列的某些部分使用fillna。根据您的描述,fillna 应该只在第一个非 NaN 之后和最后一个非 NaN 之前填充 NaN:

        import numpy as np
        import pandas as pd
        
        
        def fill_column(house):
            house = house.copy()
            non_nans = house[~house.apply(np.isnan)]
            start, end = non_nans.index[0], non_nans.index[-1]
            house.ix[start:end] = house.ix[start:end].fillna(method='ffill')
            return house
        
        
        house1 = pd.Series([np.nan, np.nan, np.nan, 200000, 200000, np.nan, np.nan, 200000, 190000, np.nan, np.nan, np.nan])
        print fill_column(house1)
        

        输出:

        0        NaN
        1        NaN
        2        NaN
        3     200000
        4     200000
        5     200000
        6     200000
        7     200000
        8     190000
        9        NaN
        10       NaN
        11       NaN
        

        请注意,这假设 Series 至少包含两个非 NaN,对应于第一天和最后一天的价格。

        【讨论】:

        • 为什么不简单地house[~house.isna()]
        【解决方案4】:

        这是一个适用于现代 pandas (>=1.1) 的函数,具有多个间隙,完全没有间隙,最重要的是 - 也适用于 .groupby()

        def fill_gap(s, method="ffill"):
            """Fills true gap in series."""
            col = s.copy()
            first_idx = col.first_valid_index()
            last_idx = col.last_valid_index()
            col.loc[first_idx:last_idx] = col.loc[first_idx:last_idx].fillna(method=method)
            return col
        

        确保索引严格升序!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-01-11
          • 2016-04-02
          • 1970-01-01
          • 2015-08-19
          • 1970-01-01
          • 2013-06-21
          • 1970-01-01
          • 2013-04-15
          相关资源
          最近更新 更多