Pandas:仅在数据帧的开头和结尾删除 NaN

问题描述 投票:0回答:5

我有一个 pandas DataFrame,如下所示:

       sum
1948   NaN
1949   NaN
1950     5
1951     3
1952   NaN
1953     4
1954     8
1955   NaN

并且我想仅在开头和结尾处切断

NaN
(即仅保留从1950年到1954年包括
NaN
在内的值)。 我已经尝试过
.isnull()
dropna()
,但不知何故我找不到合适的解决方案。 有人可以帮忙吗?

python pandas time-series dataframe nan
5个回答
55
投票

使用内置的

first_valid_index
last_valid_index
,它们是专门为此设计的,并对你的 df 进行切片:

In [5]:

first_idx = df.first_valid_index()
last_idx = df.last_valid_index()
print(first_idx, last_idx)
df.loc[first_idx:last_idx]
1950 1954
Out[5]:
      sum
1950    5
1951    3
1952  NaN
1953    4
1954    8

4
投票

这是一种方法。

import pandas as pd

# your data
# ==============================
df

      sum
1948  NaN
1949  NaN
1950    5
1951    3
1952  NaN
1953    4
1954    8
1955  NaN

# processing
# ===============================
idx = df.fillna(method='ffill').dropna().index
res_idx = df.loc[idx].fillna(method='bfill').dropna().index
df.loc[res_idx]

      sum
1950    5
1951    3
1952  NaN
1953    4
1954    8

3
投票

这是一种

Numpy
的方法:

import numpy as np

x    = np.logical_not(pd.isnull(df))
mask = np.logical_and(np.cumsum(x)!=0, np.cumsum(x[::-1])[::-1]!=0)

In [313]: df.loc[mask['sum'].tolist()]

Out[313]:
      sum
1950    5
1951    3
1952  NaN
1953    4
1954    8

1
投票

单行:

df.query('[email protected]().isna().any(axis=1)&[email protected]().isna().any(axis=1)')

0
投票

扩展李建勋的想法并使其可方法链化,我们首先,

df = pd.DataFrame(
    {
        "val": [np.nan, 1, np.nan, 2, np.nan] + [np.nan, 1, 2, 3, np.nan]
    }
)

Out[0]:

    val
0   NaN
1   1.0
2   NaN
3   2.0
4   NaN
5   NaN
6   1.0
7   2.0
8   3.0
9   NaN

然后我们可以得到idx如下,

(df.ffill().notna() & df.bfill().notna()).all(axis=1)

Out[1]:

0    False
1     True
2     True
3     True
4     True
5     True
6     True
7     True
8     True
9    False
dtype: bool

但是我们可以将它放入可链接的对象中,就像这样,

(
    df
    .loc[lambda df_: (df_.ffill().notna() & df_.bfill().notna()).all(axis=1)]
)

Out[2]:

    val
1   1.0
2   NaN
3   2.0
4   NaN
5   NaN
6   1.0
7   2.0
8   3.0

这里的美妙之处在于它可以轻松地“多列”。注意:将

.any
切换为
.all
保留 包含非 nan 值的任何外部行。

df = pd.DataFrame(
    {
        # "gp": ["a"] * 5 + ["b"] * 5,
        "val": [np.nan, 1, np.nan, 2, np.nan] + [np.nan, 1, 2, 3, np.nan],
        "val2": [1, 1, 3, 2, np.nan] + [np.nan, 1, 2, 3, np.nan]
    }
)

(
    df
    .loc[lambda df_: (df_.ffill().notna() & df_.bfill().notna()).any(axis=1)]
)

Out[3]:

    val val2
0   NaN 1.0
1   1.0 1.0
2   NaN 3.0
3   2.0 2.0
4   NaN NaN
5   NaN NaN
6   1.0 1.0
7   2.0 2.0
8   3.0 3.0

最后,它可以分组,

df = pd.DataFrame(
    {
        "gp": ["a"] * 5 + ["b"] * 5,
        "val": [np.nan, 1, np.nan, 2, np.nan] + [np.nan, 1, 2, 3, np.nan],
        "val2": [1, 1, 3, 2, np.nan] + [np.nan, 1, 2, 3, np.nan]
    }
)

(
    df
    .loc[(
        lambda df_: (
            df_
            .groupby("gp", group_keys=False)
            .apply(lambda s: s.ffill().notna() & s.bfill().notna())
            .all(axis=1)
        )
    )]
)

Out[4]:

    gp  val   val2
1   a   1.0   1.0
2   a   NaN   3.0
3   a   2.0   2.0
6   b   1.0   1.0
7   b   2.0   2.0
8   b   3.0   3.0
© www.soinside.com 2019 - 2024. All rights reserved.