我有两个大熊猫系列。
s1
潜在的大量的行和一些。NaN
和 s2
(它是数据框架中的一列(df
),只有20行。两个系列的索引不同。
s1:
id
1 4.5
2 15.0
3 13.0
4 14.0
5 18.0
6 15.0
7 13.0
8 14.0
9 NaN
10 NaN
11 NaN
12 18.0
13 NaN
14 NaN
15 NaN
df:
col1 s2
0 20.0 0.0
1 19.0 4.5
2 18.0 5.0
3 17.0 6.0
4 16.0 7.0
5 15.0 8.0
6 14.0 9.0
7 13.0 10.0
8 12.0 11.0
9 11.0 12.0
10 10.0 13.0
11 9.0 15.0
12 8.0 16.0
13 7.0 18.0
14 6.0 20.0
15 5.0 22.0
16 4.0 24.0
17 3.0 26.0
18 2.0 28.0
19 1.0 100.0
对于每个 id
的 s1
我想检索 col1
的第一个元素 s2
小于或等于 id
.
即对 id 1
我们有 s1 = 4.5
,小于或等于 df.s2 = 4.5
因此,我想检索的值是 19
因此,对于 id=2
在 s1
我需要检索的值 9
在 df.col1
这是我目前的解决方案。我想知道是否有更好(更快,也许是pandas函数?)的方法来获得同样的结果。
output = [min(df[df['s2'].le(element)].col1, default = np.NaN) for element in s1]
[19.0,
9.0,
10.0,
10.0,
7.0,
9.0,
10.0,
10.0,
nan,
nan,
nan,
7.0,
nan,
nan,
nan]
我的想法是使用numpy和比较每个值从列的每一个值的 Series
为二维数组,然后传给 numpy.where
,设置 NaN
如果没有匹配,最后一次使用 numpy.nanmean
:
m = df['s2'].to_numpy() <= s1.to_numpy()[:, None]
a = np.nanmin(np.where(m, df['col1'], np.nan), axis=1)
print (a)
[19. 9. 10. 10. 7. 9. 10. 10. nan nan nan 7. nan nan nan]
绩效:原始样本
In [63]: %%timeit
...: [min(df[df['s2'].le(element)].col1, default = np.NaN) for element in s1]
...:
...:
9.21 ms ± 305 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [64]: %%timeit
...: m = df['s2'].to_numpy() <= s1.to_numpy()[:, None]
...: a = np.nanmin(np.where(m, df['col1'], np.nan), axis=1)
72.4 µs ± 870 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
较大的数据100倍。
#2k rows
df = pd.concat([df] * 100, ignore_index=True)
#1.5k rows
s1 = pd.concat([s1] * 100, ignore_index=True)
In [68]: %%timeit
...: [min(df[df['s2'].le(element)].col1, default = np.NaN) for element in s1]
...:
...:
1.12 s ± 17.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [69]: %%timeit
...: m = df['s2'].to_numpy() <= s1.to_numpy()[:, None]
...: a = np.nanmin(np.where(m, df['col1'], np.nan), axis=1)
34.2 ms ± 305 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
你可以用区间指数。
首先是数据。
df1 = pd.DataFrame(
np.array(
[
4.5,
15.0,
13.0,
14.0,
18.0,
15.0,
13.0,
14.0,
np.nan,
np.nan,
np.nan,
18.0,
np.nan,
np.nan,
np.nan,
]
),
columns=["s1"],
)
print(df1)
s1
0 4.500
1 15.000
2 13.000
3 14.000
4 18.000
5 15.000
6 13.000
7 14.000
8 nan
9 nan
10 nan
11 18.000
12 nan
13 nan
14 nan
然后是查找数据框。
df = pd.DataFrame.from_dict(
{
"col1": {
0: 20.0,
1: 19.0,
2: 18.0,
3: 17.0,
4: 16.0,
5: 15.0,
6: 14.0,
7: 13.0,
8: 12.0,
9: 11.0,
10: 10.0,
11: 9.0,
12: 8.0,
13: 7.0,
14: 6.0,
15: 5.0,
16: 4.0,
17: 3.0,
18: 2.0,
19: 1.0,
},
"end": {
0: 0.0,
1: 4.5,
2: 5.0,
3: 6.0,
4: 7.0,
5: 8.0,
6: 9.0,
7: 10.0,
8: 11.0,
9: 12.0,
10: 13.0,
11: 15.0,
12: 16.0,
13: 18.0,
14: 20.0,
15: 22.0,
16: 24.0,
17: 26.0,
18: 28.0,
19: 100.0,
},
}
)
print(df)
col1 end
0 20.000 0.000
1 19.000 4.500
2 18.000 5.000
3 17.000 6.000
4 16.000 7.000
5 15.000 8.000
6 14.000 9.000
7 13.000 10.000
8 12.000 11.000
9 11.000 12.000
10 10.000 13.000
11 9.000 15.000
12 8.000 16.000
13 7.000 18.000
14 6.000 20.000
15 5.000 22.000
16 4.000 24.000
17 3.000 26.000
18 2.000 28.000
19 1.000 100.000
做一个起始列来创建区间,第一行填上零。
df["start"] = df["end"].shift().fillna(0)
print(df.head())
col1 end start
0 20.000 0.000 0.000
1 19.000 4.500 0.000
2 18.000 5.000 4.500
3 17.000 6.000 5.000
4 16.000 7.000 6.000
创建一个区间索引,并设置为索引。
idx = pd.IntervalIndex.from_arrays(df["start"], df["end"], closed="right")
df.index = idx
print(df.head())
col1 end start
(0.0, 0.0] 20.000 0.000 0.000
(0.0, 4.5] 19.000 4.500 0.000
(4.5, 5.0] 18.000 5.000 4.500
(5.0, 6.0] 17.000 6.000 5.000
(6.0, 7.0] 16.000 7.000 6.000
最终结果
df1.loc[df1.dropna().index, "col1"] = df.loc[df1.loc[:, "s1"].dropna(), "col1"].values
print(df1)
s1 col1
0 4.500 19.000
1 15.000 9.000
2 13.000 10.000
3 14.000 9.000
4 18.000 7.000
5 15.000 9.000
6 13.000 10.000
7 14.000 9.000
8 nan nan
9 nan nan
10 nan nan
11 18.000 7.000
12 nan nan
13 nan nan
14 nan nan
完整的代码没有打印出来。
df["start"] = df["end"].shift().fillna(0)
idx = pd.IntervalIndex.from_arrays(df["start"], df["end"], closed="right")
df.index = idx
df1.loc[df1.dropna().index, "col1"] = df.loc[df1.loc[:, "s1"].dropna(), "col1"].values