首页 > 解决方案 > 使用 Numpy 数组或 Pandas 数据框查找满足条件的第一行。(可能很棘手)

问题描述

我将以与图书馆无关的方式提出这个问题,因为在这种情况下,一个可能比另一个更好。或者也许存在另一个神奇的图书馆?

我有一个包含大约 10,000 条记录的数据库表,并且我知道如何从中创建一个 numpy 数组或数据框。数据是这样的。

   ...
   20,25,1,5
   20,25,2,3
   20,25,4,21
   20,25,5,1
   20,25,9,19
   ...
   45,47,6,20
   45,47,10,2
   45,47,11,56
   45,47,21,41
   ...

在下面的示例搜索条件中,我在该行的 col4 中的值“20”之后。

45,47,6,20
  1. 请注意前 2 列具有相同的值并定义了一个组。
  2. Col2 将始终 >= 到 col1 连续。
  3. col3 中的值在组内总是按升序排列,不一定是连续的。

我使用以下搜索条件在 col4 中的单元格值之后。

我知道如何在 numpy 中使用掩码来查找其值为例如 'col1 >= 45 AND col2 <= 47' 的所有行。我有第三个搜索值,例如“8”,用于在上述组中搜索 col3(col1、col2、45 -> 47)

我需要找到 col3 <= 8 中的值的第一行。

因此,我需要在 col3 DESCENDING 序列中搜索具有 'col1 >= 45 AND col2 <= 47' 的行,直到找到行 '45,47,6,20'。我在 col4 中的值“20”之后。

最多只有 1 行匹配。可能没有行与条件匹配(例如,如果 col3 搜索值为“3”)。

我需要一次进行 1000 次搜索中的 100 次,因此我希望不要创建新的数组或数据帧,除非这对资源的影响最小。

标签: pythonpandasnumpy-ndarray

解决方案


我建议对前三列使用多索引,并在此多索引上使用掩码,如下所示:

# I reproduce a similar dataframe
import pandas as pd
import numpy as np
np.random.seed(123)
v1 = np.random.randint(0, 10, 10)
v2 = v1 + 2
v3 = np.random.randint(0, 10, 10)
v4 = np.random.randint(0, 10, 10)
df = pd.DataFrame({"v1": v1,
                   "v2": v2,
                   "v3": v3,
                   "v4": v4})
# and sort it according to your comments
df = df.sort_values(by=["v1", "v2", "v3"])
df.head()

我得到以下数据框:

  v1  v2  v3  v4
8   0   2   4   0
7   1   3   0   8
9   1   3   1   7
3   1   3   9   4
1   2   4   0   3

# parameters for research
val1 = 1 # the equivalent of your 45
val2 = 3 # the equivalent of your 47
val3 = 2 # the equivalent of your 8

# Set the multiindex
hdf = df.set_index(["v1", "v2", "v3"]).sort_index(ascending=False)
hdf.tail()

您的数据框现在如下所示:

          v4
v1 v2 v3    
2  4  0    3
1  3  9    4
      1    7
      0    8
0  2  4    0
# Define the mask
mask = (hdf.index.get_level_values("v1") >= val1) & \
        (hdf.index.get_level_values("v2") <= val2) &\
        (hdf.index.get_level_values("v3") <= val3)

# Select only the first row returned by the selection using cumsum on mask
print(hdf.loc[mask & (mask.cumsum() == 1), ["v4"]])

你得到:

          v4
v1 v2 v3    
1  3  1    7

推荐阅读