使用 Pandas Dataframe 对由索引分隔的部分进行插值

4

我的示例代码如下:

import pandas as pd


dictx = {'col1':[1,'nan','nan','nan',5,'nan',7,'nan',9,'nan','nan','nan',13],\
    'col2':[20,'nan','nan','nan',22,'nan',25,'nan',30,'nan','nan','nan',25],\
    'col3':[15,'nan','nan','nan',10,'nan',14,'nan',13,'nan','nan','nan',9]}
df = pd.DataFrame(dictx).astype(float)

我正在尝试插值包含值“NaN”的各个部分。
为了背景,我正在使用城市(巴西圣保罗)提供的GPS数据跟踪公交车速度,但数据稀缺且有些部分未提供信息,例如,但我确实知道有一些段是停止的,例如黎明时段,但信息也被表示为“NaN”。
我需要:
我一直在尝试使用dataframe.interpolate()参数(limit和limit_direction),但没有成功。如果我设置df.interpolate(limit=2),不仅会插值我需要的数据,还会插值我不需要的数据。因此,我需要在限制范围内对段进行插值。
期望输出:
Out[7]: 
    col1   col2   col3
0    1.0  20.00  15.00
1    nan  nan    nan
2    nan  nan    nan
3    nan  nan    nan
4    5.0  22.00  10.00
5    6.0  23.50  12.00
6    7.0  25.00  14.00
7    8.0  27.50  13.50
8    9.0  30.00  13.00
9    nan  nan    nan
10   nan  nan    nan
11   nan  nan    nan
12   13.0 25.00  9.00

我一直在尝试应用的逻辑基本上是尝试找到nan并计算它们的索引之间的差异,然后创建一个新的dataframe_temp来进行插值,然后再将其添加到另一个创建新的dataframe_final中。但由于'nan'=='nan'返回False,因此这变得很难实现。


所以逻辑是,仅插值一个或两个“nan”值的序列,但保留较长序列的原样,对吗? - jdehesa
基本上就是这样,但在原始代码中,这些“nan”序列可能高达30个,有时甚至达到60个! - Gabriel_Koch
1
更新答案以使其更加通用。 - piRSquared
3个回答

1
这是一个hack,但仍然可能有用。很可能Pandas 0.23会有更好的解决方案。

https://pandas-docs.github.io/pandas-docs-travis/whatsnew.html#dataframe-interpolate-has-gained-the-limit-area-kwarg

df_fw = df.interpolate(limit=1)
df_bk = df.interpolate(limit=1, limit_direction='backward')

df_fw.where(df_bk.notna())

    col1  col2  col3
0    1.0  20.0  15.0
1    NaN   NaN   NaN
2    NaN   NaN   NaN
3    NaN   NaN   NaN
4    5.0  22.0  10.0
5    6.0  23.5  12.0
6    7.0  25.0  14.0
7    8.0  27.5  13.5
8    9.0  30.0  13.0
9    NaN   NaN   NaN
10   NaN   NaN   NaN
11   NaN   NaN   NaN
12  13.0  25.0   9.0

不是黑客行为
更正当的处理方式。
通用于处理任何限制。
def interp(df, limit):
    d = df.notna().rolling(limit + 1).agg(any).fillna(1)
    d = pd.concat({
        i: d.shift(-i).fillna(1)
        for i in range(limit + 1)
    }).prod(level=1)

    return df.interpolate(limit=limit).where(d.astype(bool))

df.pipe(interp, 1)

    col1  col2  col3
0    1.0  20.0  15.0
1    NaN   NaN   NaN
2    NaN   NaN   NaN
3    NaN   NaN   NaN
4    5.0  22.0  10.0
5    6.0  23.5  12.0
6    7.0  25.0  14.0
7    8.0  27.5  13.5
8    9.0  30.0  13.0
9    NaN   NaN   NaN
10   NaN   NaN   NaN
11   NaN   NaN   NaN
12  13.0  25.0   9.0

可以处理不同列中NaN的变化。考虑一个不同的df。
dictx = {'col1':[1,'nan','nan','nan',5,'nan','nan',7,'nan',9,'nan','nan','nan',13],\
    'col2':[20,'nan','nan','nan',22,'nan',25,'nan','nan',30,'nan','nan','nan',25],\
    'col3':[15,'nan','nan','nan',10,'nan',14,'nan',13,'nan','nan','nan',9,'nan']}
df = pd.DataFrame(dictx).astype(float)
df

    col1  col2  col3
0    1.0  20.0  15.0
1    NaN   NaN   NaN
2    NaN   NaN   NaN
3    NaN   NaN   NaN
4    5.0  22.0  10.0
5    NaN   NaN   NaN
6    NaN  25.0  14.0
7    7.0   NaN   NaN
8    NaN   NaN  13.0
9    9.0  30.0   NaN
10   NaN   NaN   NaN
11   NaN   NaN   NaN
12   NaN   NaN   9.0
13  13.0  25.0   NaN

然后使用 limit=1
df.pipe(interp, 1)

    col1  col2  col3
0    1.0  20.0  15.0
1    NaN   NaN   NaN
2    NaN   NaN   NaN
3    NaN   NaN   NaN
4    5.0  22.0  10.0
5    NaN  23.5  12.0
6    NaN  25.0  14.0
7    7.0   NaN  13.5
8    8.0   NaN  13.0
9    9.0  30.0   NaN
10   NaN   NaN   NaN
11   NaN   NaN   NaN
12   NaN   NaN   9.0
13  13.0  25.0   9.0

而且使用 limit=2

df.pipe(interp, 2).round(2)

     col1   col2  col3
0    1.00  20.00  15.0
1     NaN    NaN   NaN
2     NaN    NaN   NaN
3     NaN    NaN   NaN
4    5.00  22.00  10.0
5    5.67  23.50  12.0
6    6.33  25.00  14.0
7    7.00  26.67  13.5
8    8.00  28.33  13.0
9    9.00  30.00   NaN
10    NaN    NaN   NaN
11    NaN    NaN   NaN
12    NaN    NaN   9.0
13  13.00  25.00   9.0

非常感谢您的帮助,我想知道您是如何学习的?您使用书籍吗?我一直在努力提高我的编程能力,但我不认为我会像您和@unutbu提出的那样。 - Gabriel_Koch

1
以下是一种有选择地忽略连续NaN行的方法,其长度大于某个特定大小(由limit给出):
import numpy as np
import pandas as pd

dictx = {'col1':[1,'nan','nan','nan',5,'nan',7,'nan',9,'nan','nan','nan',13],\
    'col2':[20,'nan','nan','nan',22,'nan',25,'nan',30,'nan','nan','nan',25],\
    'col3':[15,'nan','nan','nan',10,'nan',14,'nan',13,'nan','nan','nan',9]}
df = pd.DataFrame(dictx).astype(float)

limit = 2
notnull = pd.notnull(df).all(axis=1)
# assign group numbers to the rows of df. Each group starts with a non-null row,
# followed by null rows
group = notnull.cumsum()
# find the index of groups having length > limit
ignore = (df.groupby(group).filter(lambda grp: len(grp)>limit)).index
# only ignore rows which are null
ignore = df.loc[~notnull].index.intersection(ignore)
keep = df.index.difference(ignore)
# interpolate only the kept rows
df.loc[keep] = df.loc[keep].interpolate()

print(df)

打印

    col1  col2  col3
0    1.0  20.0  15.0
1    NaN   NaN   NaN
2    NaN   NaN   NaN
3    NaN   NaN   NaN
4    5.0  22.0  10.0
5    6.0  23.5  12.0
6    7.0  25.0  14.0
7    8.0  27.5  13.5
8    9.0  30.0  13.0
9    NaN   NaN   NaN
10   NaN   NaN   NaN
11   NaN   NaN   NaN
12  13.0  25.0   9.0

通过改变limit的值,您可以控制组的大小达到多少才应该被忽略。

0

这是部分答案。

for i in list(df):

     for x in range(len(df[i])):

         if not df[i][x]  > -100:

                   df[i][x] = 0

df

col1    col2    col3
0   1.0     20.0    15.0
1   0.0     0.0     0.0
2   0.0     0.0     0.0
3   0.0     0.0     0.0
4   5.0     22.0    10.0
5   0.0     0.0     0.0
6   7.0     25.0    14.0
7   0.0     0.0     0.0
8   9.0     30.0    13.0
9   0.0     0.0     0.0
10  0.0     0.0     0.0
11  0.0     0.0     0.0
12  13.0    25.0    9.0

现在,

df["col1"][1] == df["col2"][1]
True

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接