使用 pandas 仅填充空白,而不是末端的 NaN
Using pandas to fill gaps only, and not NaNs on the ends
我有一些大约 8 个月的房价数据,跟踪房屋进入市场直至售出的价格。我想填补中间数据中的几个空白,但我想保留每个数据末尾的 NaN 不变。
举个简单的例子,假设我们的房子 1 在 'Day 4' 以 200000 的价格上市,并在 'Day 9' 以 190000 的价格出售。我们的房子 2 在第 1 天到第 12 天保持在 180000,并且在那段时间没有出售 window。但是,第 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 数据帧中的列。
麻烦的是,我通常用来填补空白的函数是 DataFrame.fillna() 使用 backfill 或 ffill 方法。如果我使用 ffill,house1 returns 这个:
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]
它再次填补了空白,但随后它也开始填充超出 'real' 数据结束位置的数据。
到目前为止,我的解决方案是编写以下函数:
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 函数?
您可以在系列的某些部分使用 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
请注意,这假设系列包含至少两个非 NaN,对应于第一天和最后一天的价格。
一年后我找到了这个答案,但需要它在具有多列的 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
谢谢!
多列 DataFrame 的另一种解决方案
df.fillna(method='ffill') + (df.fillna(method='bfill') * 0)
它是如何工作的?
第一个 fillna
对值进行前向填充。这几乎就是我们想要的,只是它在每个系列的末尾留下了填充值的踪迹。
第二个 fillna
向后填充我们乘以零的值。结果是我们不需要的尾随值将是 NaN,其他所有值都将是 0。
最后,我们将两者相加,利用 x + 0 = x 和 x + NaN = NaN 的事实。
这是一个适用于现代 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
确保索引严格升序!
我有一些大约 8 个月的房价数据,跟踪房屋进入市场直至售出的价格。我想填补中间数据中的几个空白,但我想保留每个数据末尾的 NaN 不变。
举个简单的例子,假设我们的房子 1 在 'Day 4' 以 200000 的价格上市,并在 'Day 9' 以 190000 的价格出售。我们的房子 2 在第 1 天到第 12 天保持在 180000,并且在那段时间没有出售 window。但是,第 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 数据帧中的列。
麻烦的是,我通常用来填补空白的函数是 DataFrame.fillna() 使用 backfill 或 ffill 方法。如果我使用 ffill,house1 returns 这个:
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]
它再次填补了空白,但随后它也开始填充超出 'real' 数据结束位置的数据。
到目前为止,我的解决方案是编写以下函数:
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 函数?
您可以在系列的某些部分使用 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
请注意,这假设系列包含至少两个非 NaN,对应于第一天和最后一天的价格。
一年后我找到了这个答案,但需要它在具有多列的 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
谢谢!
多列 DataFrame 的另一种解决方案
df.fillna(method='ffill') + (df.fillna(method='bfill') * 0)
它是如何工作的?
第一个 fillna
对值进行前向填充。这几乎就是我们想要的,只是它在每个系列的末尾留下了填充值的踪迹。
第二个 fillna
向后填充我们乘以零的值。结果是我们不需要的尾随值将是 NaN,其他所有值都将是 0。
最后,我们将两者相加,利用 x + 0 = x 和 x + NaN = NaN 的事实。
这是一个适用于现代 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
确保索引严格升序!