pandas在两个数列之间进行元素比较的最佳方法。

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

我有两个大熊猫系列。

s1 潜在的大量的行和一些。NaNs2 (它是数据框架中的一列(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

对于每个 ids1 我想检索 col1 的第一个元素 s2 小于或等于 id.

即对 id 1 我们有 s1 = 4.5,小于或等于 df.s2 = 4.5因此,我想检索的值是 19因此,对于 id=2s1 我需要检索的值 9df.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]

python pandas dataframe
1个回答
3
投票

我的想法是使用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)

1
投票

你可以用区间指数。

首先是数据。

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
© www.soinside.com 2019 - 2024. All rights reserved.