pandas 在Python中进行过滤和选择的聪明方法

a0zr77ik  于 11个月前  发布在  Python
关注(0)|答案(2)|浏览(83)

我正在努力寻找在Python中执行此操作的最有效方法。我将感谢所有建议。
我有一个数字数据数组;有些元素是NaN。当我找到一个NaN时,我想选择20个最接近的元素,这些元素是有效的数值。所以,在理想情况下,NaN数据点位于中间的某个地方,它之后的10个元素和它之前的10个元素都是有效的数值。在一个更棘手的情况下,NaN前面只有5个元素,我必须选择NaN之后的15个元素。更棘手的情况是NaN之前只有5个元素,而原始NaN之后的15个元素中也有一个NaN,所以我必须选择16个元素(然后过滤掉NaN,得到我需要的15个元素)。
我可以通过从一个嵌套框架中过滤出NaN并保留一个新的原始索引列来做到这一点,但这很混乱,而且我还不是pandas和numpy的Maven。我觉得有一种聪明的方法可以做到这一点。
谢谢你

ssm49v7z

ssm49v7z1#

假设这个例子:

col
0    NaN
1    0.0
2    1.0
3    2.0
4    NaN
5    NaN
6    3.0
7    4.0
8    5.0
9    NaN
10   6.0
11   NaN
12   7.0
13   8.0
14   9.0
15   NaN
16  10.0

字符串
您可以使用掩码ffillsliding_window_viewjoin的组合:

from numpy.lib.stride_tricks import sliding_window_view as swv

B = 2  # number of non-NA values before
A = 3  # after

# extract column as series
# ensure range index
s = df['col'].reset_index(drop=True)

# identify non-NA rows
m = s.notna()

# mask the indices of NAs and ffill
idx = s.index.to_series().where(m).ffill()

# craft a DataFrame from a sliding window view
tmp = pd.DataFrame(swv(s[m], A+B), index=idx[m].shift(-B+1)[:m.sum()-(A+B)+1])

# reindex with filled indices, set back original index, join
out = df.join(tmp.reindex(idx).set_axis(df.index).mask(m))


输出量:

col    0    1    2    3    4
0    NaN  NaN  NaN  NaN  NaN  NaN
1    0.0  NaN  NaN  NaN  NaN  NaN
2    1.0  NaN  NaN  NaN  NaN  NaN
3    2.0  NaN  NaN  NaN  NaN  NaN
4    NaN  1.0  2.0  3.0  4.0  5.0
5    NaN  1.0  2.0  3.0  4.0  5.0
6    3.0  NaN  NaN  NaN  NaN  NaN
7    4.0  NaN  NaN  NaN  NaN  NaN
8    5.0  NaN  NaN  NaN  NaN  NaN
9    NaN  4.0  5.0  6.0  7.0  8.0
10   6.0  NaN  NaN  NaN  NaN  NaN
11   NaN  5.0  6.0  7.0  8.0  9.0
12   7.0  NaN  NaN  NaN  NaN  NaN
13   8.0  NaN  NaN  NaN  NaN  NaN
14   9.0  NaN  NaN  NaN  NaN  NaN
15   NaN  NaN  NaN  NaN  NaN  NaN
16  10.0  NaN  NaN  NaN  NaN  NaN

e4yzc0pl

e4yzc0pl2#

我理解,对于每一行,你想向前看10行,并使用它的值,如果它是NA,你想采取下一个非NA值,跟随这一行,同样向前。
假设你有一个列value,你想操作它,你可以这样做:

shift_b = 10
shift_f = 15
ser_backward = df["value"].ffill().shift(shift_b)
ser_forward = df["value"].bfill().shift(-shift_f)
df["backward"] = ser_backward
df["forward"] = ser_forward

字符串
这将向前和向后查看,并假设您在数据集的开头和结尾应用相同的间隙。如果不是这种情况,则可以应用ffillbfill(见本文结尾)。shift方法做了主要的技巧。如果你不需要跳过NA值,它就足够了。因为你需要跳过前瞻,你需要向后传播前向的非NA值,以便为每个NA值使用下一个非NA值。而在锁定情况下则恰恰相反。

测试数据

例如,我使用shift_f=shift_b=3(因此向后和向前锁定仅3步而不是10步)来可视化较小数据集的效果。让我们假设我们对名为value的列应用该方法感兴趣。我们最初使用与该行索引相等的值填充列中的某些值,以便可以很容易地看到发生了什么。

import pandas as pd
from random import sample
df = pd.DataFrame(index=list(range(20)))
non_null_indexes = [5, 19, 10, 4, 3, 2, 7, 13, 17]
df.loc[non_null_indexes, "value"] = non_null_indexes

结果:

| | 落后|向前| forward |
| --|--|--|--|
| 0 |楠|楠| 3 |
| 1 |楠|楠| 4 |
| 2 | 2 |楠| 5 |
| 3 | 3 |楠| 7 |
| 4 | 4 |楠| 7 |
| 5 | 5 | 2 | 10 |
| 6 |楠| 3 | 10 |
| 7 | 7 | 4 | 10 |
| 8 |楠| 5 | 13 |
| 9 |楠| 5 | 13 |
| 10 | 10 | 7 | 13 |
| 11 |楠| 7 | 17 |
| 12 |楠| 7 | 17 |
| 13 | 13 | 10 | 17 |
| 14 |楠| 10 | 17 |
| 15 |楠| 10 | 19 |
| 16 |楠| 13 | 19 |
| 17 | 17 | 13 |楠|
| 18 |楠| 13 |楠|
| 19 | 19 | 13 |楠|

数据集开始和结束的替代处理

如果你想使用第一个可用值而不是NA,你只需要像这样添加另一个bfill/ffill

shift = 3
ser_backward = df["value"].ffill().shift(shift_b).bfill()
ser_forward = df["value"].bfill().shift(-shift_f).ffill()
df["backward"] = ser_backward
df["forward"] = ser_forward


然后,我们在数据集的开始处有一个较短的lockback,在数据集的结束处有一个较短的lookforward,结果如下所示:
| | 落后|向前| forward |
| --|--|--|--|
| 0 |楠| 2 | 3 |
| 1 |楠| 2 | 4 |
| 2 | 2 | 2 | 5 |
| 3 | 3 | 2 | 7 |
| 4 | 4 | 2 | 7 |
| 5 | 5 | 2 | 10 |
| 6 |楠| 3 | 10 |
| 7 | 7 | 4 | 10 |
| 8 |楠| 5 | 13 |
| 9 |楠| 5 | 13 |
| 10 | 10 | 7 | 13 |
| 11 |楠| 7 | 17 |
| 12 |楠| 7 | 17 |
| 13 | 13 | 10 | 17 |
| 14 |楠| 10 | 17 |
| 15 |楠| 10 | 19 |
| 16 |楠| 13 | 19 |
| 17 | 17 | 13 | 19 |
| 18 |楠| 13 | 19 |
| 19 | 19 | 13 | 19 |

相关问题