【问题标题】:Element wise numeric comparison in Pandas dataframe column value with listPandas数据框列值与列表中的元素明智数值比较
【发布时间】:2021-06-06 08:06:55
【问题描述】:

我有 3 个 pandas 多索引列数据框

数据框1(最小值):

  |  A    |   B   |  C   |
  |  Min  |  Min  |  Min |
  |-------|-------|------|
0 | 26.47 | 17.31 | 1.26 |
1 | 27.23 | 14.38 | 1.36 |
2 | 27.23 | 18.88 | 1.28 |

数据框 2(用于比较的值)

第 0 行、第 1 行和第 2 行相同,我将数据帧扩展到三行,以便与最小和最大数据帧进行比较。每个数据框单元格中的值是 ndarray

  |          A          |           B           |          C         |
  |         Val         |          Val          |         Val        |
  |---------------------|-----------------------|--------------------|
0 | [27.58,28.37,28.73] | [17.31, 18.42, 18.72] | [1.36, 1.28, 1.27] |
1 | [27.58,28.37,28.73] | [17.31, 18.42, 18.72] | [1.36, 1.28, 1.27] |
2 | [27.58,28.37,28.73] | [17.31, 18.42, 18.72] | [1.36, 1.28, 1.27] |

数据帧3(最大值):

  |  A    |   B   |  C   |
  |  Max  |  Max  |  Max |
  |-------|-------|------|
0 | 28.68 | 18.42 | 1.37 |
1 | 29.50 | 17.31 | 1.47 |
2 | 29.87 | 20.45 | 1.39 |

预期结果:

  |          A          |           B           |          C           |
  |        Result       |          Result       |         Result       |
  |---------------------|-----------------------|----------------------|
0 | [True, True, False] |  [True, True, False]  | [True, True, True]   |
1 | [True, True, True]  | [True, False, False]  | [True, False, False] |
2 | [True, True, True]  | [False, False, False] | [True, True, False]  |

我想以这种方式进行元素比较:

min <= each element in ndarray <= max

即

for row 0:

26.47 <= [27.58,28.37,28.73] <= 28.68

17.31 <= [17.31, 18.42, 18.72] <= 18.42

1.26 <= [1.36, 1.28, 1.27] <= 1.37

等等

我试过( datafram2 &gt;= dataframe3 ) &amp; ( datafram2 &lt;= datafram3 ),但没用。

计算结果最简单和最快的方法是什么?

示例数据框代码:

min_columns = pd.MultiIndex.from_product( [ [ 'A', 'B', 'C' ], [ 'Min' ] ] )
val_columns = pd.MultiIndex.from_product( [ [ 'A', 'B', 'C' ], [ 'Val' ] ] )
max_columns = pd.MultiIndex.from_product( [ [ 'A', 'B', 'C' ], [ 'Max' ] ] )

min_df = pd.DataFrame( [ [ 26.47, 17.31, 1.26 ], [ 27.23, 14.38, 1.36 ], [ 27.23, 18.88, 1.28 ] ], columns=min_columns )
val_df = pd.DataFrame( [ [ [ 27.58, 28.37, 28.73 ], [ 17.31, 18.42, 18.72], [1.36, 1.28, 1.27 ] ] ] , columns=val_columns )
max_df = pd.DataFrame( [ [ 28.68, 18.42, 1.37 ], [ 29.50, 17.31, 1.47 ], [ 29.87, 20.45, 1.39 ] ] , columns=max_columns )

【问题讨论】:

  • 请贴出本例中用于生成数据帧的代码。
  • 旁注:在数据框中保存列表/数组通常是不好的做法。这会阻止使用 pandas api 的完整表达式,产生性能不佳的操作,并且通常会使事情变得更复杂。
  • @anon01 因为我想将列表中的每个元素与不同的最大值和最小值组合进行比较,所以我必须将要比较的元素分组到列表中,并将不同的最小值和最大值分开数据框。我是 python 的新手,不确定是否有有效的验证方式来实现这一点,尽管我知道apply 函数性能不太好。

标签: python pandas dataframe numpy


【解决方案1】:

只需将列值转换为 NumPy 数组即可。并简单地将其视为数组比较问题(按行)。

你可以使用apply:

def bool_check(row):
    col = row.name[0]
    min_val = df1[pd.IndexSlice[col]].to_numpy()
    max_val = df3[pd.IndexSlice[col]].to_numpy()
    x = np.array(row.tolist())
    return list((x >= min_val) & (x <= max_val))

res = df2.apply(bool_check,axis=0).rename(columns={'Val':'Result'})

分辨率:

A B C
Result Result Result
0 [True, True, False] [True, True, False] [True, True, True]
1 [True, True, True] [True, False, False] [True, False, False]
2 [True, True, True] [False, False, False] [True, True, False]

更新

(基于您提供的数据的完整解决方案):

def bool_check(row):
    col = row.name[0]
    min_val = min_df[pd.IndexSlice[col]].to_numpy()
    max_val = max_df[pd.IndexSlice[col]].to_numpy()
    x = np.array(row.tolist())
    return list((x >= min_val) & (x <= max_val))

res = val_df.apply(bool_check,axis=0).rename(columns={'Val':'Result'})

时间对比:

方法一(Nk03的方法一):

CPU 时间:用户 19.5 毫秒,系统:0 ns,总计:19.5 毫秒挂壁时间:18.9 毫秒

方法2(Nk03的方法2):

CPU 时间:用户 23 毫秒,系统:102 微秒,总计:23.1 毫秒挂壁时间:21.9 毫秒

方法3(使用基于numpy的比较):

CPU 时间:用户 8.76 毫秒,系统:26 微秒,总计:8.79 毫秒挂壁时间:8.91 毫秒

Nk03 的更新优化解决方案:

CPU 时间:用户 16 毫秒,系统:0 ns,总计:16 毫秒挂壁时间:15.5 毫秒

【讨论】:

    【解决方案2】:

    如果你有这样的数据框:

    df1 = pd.DataFrame({'AMin': {0: 26.47, 1: 27.23, 2: 27.23},
     'BMin': {0: 17.31, 1: 14.38, 2: 18.88},
     'CMin': {0: 1.26, 1: 1.36, 2: 1.28}})
    
    
    df2 = pd.DataFrame({'AVal': {0: [27.58, 28.37, 28.73],
      1: [27.58, 28.37, 28.73],
      2: [27.58, 28.37, 28.73]},
     'BVal': {0: [17.31, 18.42, 18.72],
      1: [17.31, 18.42, 18.72],
      2: [17.31, 18.42, 18.72]},
     'CVal': {0: [1.36, 1.28, 1.27], 1: [1.36, 1.28, 1.27], 2: [1.36, 1.28, 1.27]}})
    
    df3 = pd.DataFrame({'AMax': {0: 28.68, 1: 29.5, 2: 29.87},
     'BMax': {0: 18.42, 1: 17.31, 2: 20.45},
     'CMax': {0: 1.37, 1: 1.47, 2: 1.39}})
    

    然后你可以explode第二个数据框并比较值。

    m = df2.apply(pd.Series.explode).values
    df = pd.DataFrame(
        (df1.iloc[np.arange(len(df1)).repeat(3)].values <= m) &
        (m <= df3.iloc[np.arange(len(df3)).repeat(3)].values),
        columns=df2.columns
    )
    
    df = df.groupby(df.index // 3).agg(list)
    

    输出:

                         A                      B                     C
    0  [True, True, False]    [True, True, False]    [True, True, True]
    1   [True, True, True]   [True, False, False]  [True, False, False]
    2   [True, True, True]  [False, False, False]   [True, True, False]
    

    【讨论】:

    • 多次使用Apply会使代码变得异常缓慢。
    • @Pygirl 这就是我添加 2 个选项的原因。第一个没有apply。与您的答案相比,它仍然很慢:)
    • @Pygirl 对第一个选项进行了一些更改。如果您不介意,您也可以比较更新选项的性能吗?
    • 完成^_^。你想节省线路吗? :P 通过使用df2.apply(pd.Series.explode) 两次:P
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2021-11-10
    相关资源
    最近更新 更多