我有一个 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()
,但不知何故我找不到合适的解决方案。
有人可以帮忙吗?
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
这是一种方法。
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
这是一种
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
单行:
df.query('[email protected]().isna().any(axis=1)&[email protected]().isna().any(axis=1)')
扩展李建勋的想法并使其可方法链化,我们首先,
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