np.where: "ValueError: operands could not be broadcast together with shapes (38658637,) (9456,)"

np.where: "ValueError: operands could not be broadcast together with shapes (38658637,) (9456,)"

我有两个具有两种不同形状的数据框:

  1. df_rts_1 #Shape: (38658637, 7)
  2. df_crsh_rts #Shape: (9456, 6)

我正在尝试使用 np.where 来更新列值 (df_rts_1['crash'])根据一定条件等于1如下:

  1. df_rts_1['tmc_code']= df_crsh_rts['tmc']
  2. df_rts_1['measurement_tstamp'] is between df_crsh_rts['Start_time'] and df_crsh_rts['Closed_time']

我的代码:

df_rts_1['crash'] = np.where((df_rts_1['tmc_code'].values == df_crsh_rts['tmc'].values) & ((df_rts_1['measurement_tstamp'].values > df_crsh_rts['Start_time'].values) & (df_rts_1['measurement_tstamp'].values > df_crsh_rts['Closed_time'].values)), 1, df_rts_1['crash'])

我在标题中遇到错误。我对 Python/data 科学还很陌生,因此非常感谢您的帮助。

假设你的两个 DataFrame 都包含:

  1. df_rts_1:

        tmc_code  measurement_tstamp  crash
     0         1 2020-01-03 10:05:00      0
     1         1 2020-01-03 11:00:00      0
     2         1 2020-01-03 12:10:00      0
     3         2 2020-01-03 10:10:00      0
     4         3 2020-01-03 10:05:00      0
    
  2. df_crsh_rts:

        tmc          Start_time         Closed_time
     0    1 2020-01-03 10:00:00 2020-01-03 11:00:00
     1    2 2020-01-03 14:00:00 2020-01-03 15:00:00
     2    4 2020-01-03 16:00:00 2020-01-03 18:00:00
    

为了便于评估“介于”条件,让我们创建以下内容 区间索引:

interv = pd.IntervalIndex.from_arrays(df_crsh_rts.Start_time,
    df_crsh_rts.Closed_time, closed='both')

现在,假设我们有来自 df_rts_1 的当前 ,让我们构建 您的情况:

  • “介于”条件可以表示为interv.contains(row.measurement_tstamp),
  • tmc / tmc_code值的等式可以表示为 df_crsh_rts.tmc.eq(row.tmc_code).

要检查它们是如何工作的,将 df_rts_1 的第一行保存为 变量:

row = df_rts_1.iloc[0]

并执行两个条件。

第一个条件生成一个 Numpy 数组 bool 类型:

array([ True, False, False])

和第二个 - Series(也是 bool 类型):

0     True
1    False
2    False
Name: tmc, dtype: bool

所以要构建最终的(单)bool值——这一行是否 应该已经更新了它的 crash 列,条件是:

(interv.contains(row.measurement_tstamp) & df_crsh_rts.tmc.eq(row.tmc_code)).any()

即以上两个条件的逻辑 ANDany() - 是否 any 这个连词的元素是 True.

与您的代码相比,最后的变化是:

  • 而不是 where,使用 iloc[...] 其中第一个参数(行 selector) 是上面的复合条件,为每一行计算 来自 df_rts_1(使用列表理解)- 要更新的行。
  • 第二个参数只是 crash - 要更新的列名。
  • 在指定的单元格中保存 1

执行此操作的代码是:

df_rts_1.loc[[ (interv.contains(row.measurement_tstamp) &
    df_crsh_rts.tmc.eq(row.tmc_code)).any()
    for row in df_rts_1.itertuples()], 'crash'] = 1

对于我的样本数据,结果是:

   tmc_code  measurement_tstamp  crash
0         1 2020-01-03 10:05:00      1
1         1 2020-01-03 11:00:00      1
2         1 2020-01-03 12:10:00      0
3         2 2020-01-03 10:10:00      0
4         3 2020-01-03 10:05:00      0

在评论中编辑以下问题

Q1。我们是否使用来自两个条件的索引来访问和更新 df_rts_1 数据框?

实际上不是。注意:

[ (interv.contains(row.measurement_tstamp) &
    df_crsh_rts.tmc.eq(row.tmc_code)).any() for row in df_rts_1.itertuples() ]

产生一个 list of bools,它甚至不包含原始索引。 然后在 .loc[...] 中使用,所以这是 boolean 的情况 索引。此列表的连续 True / False 元素与 来自 df_rts_1 的连续行并说明特定行是否 将 selected.

Q2Q3any() 在这里做什么? any() 对我们有什么帮助 实现。

查看初始行的示例:

  • 第一个条件(单独)说明是否 measurement_tstamp 在 当前 df_crsh_rts,
  • 的连续行中的两个日期之间
  • 第二个条件(单独)说明当前是否有匹配 tmcdf_crsh_rts.
  • 的连续行中

我们要求两个这些条件必须满足同一行 来自 df_crsh_rts,因此 & 加入了他们。

但请注意:

  • condition_1 & condition_2 产生 Series of bool 类型 - 是否 连续行满足两个部分条件:

    0    True
    1    False
    2    False
    Name: tmc, dtype: bool
    
  • 但是我们不需要 bool 的任何 Series。我们需要一个 single bool, 说明上述结果是否包含至少一个 True 值。

还有这个转换(从 bool Seriessingle bool) 由 any().

执行

编辑 2

由于你的数据量很大,我想出了另一个,可能更快 解决方案。想法是:

  • df_rts_1tmc_code,
  • 只检查 between 条件 tmc.
  • 为了加快 select 适当的间隔,使用辅助 Series tmc 索引的间隔(按索引搜索更快)。

为此,定义以下函数,应用于每个组:

def newCrash(grp):
    # Intervals for the current tmc_code
    wrk = intrv[intrv.index == grp.iloc[0,0]]
    if wrk.empty:
        return grp.crash  # Nothing found - no change
    wrkInd = pd.IntervalIndex(wrk)
    return grp.crash.mask([ wrkInd.contains(ts).any()
        for ts in grp.measurement_tstamp ], 1)

然后创建辅助系列:

intrv = pd.Series(pd.IntervalIndex.from_arrays(df_crsh_rts.Start_time,
    df_crsh_rts.Closed_time, closed='both'), index=df_crsh_rts.tmc)

最后 运行 崩溃 列的更新:

df_rts_1.crash = df_rts_1.groupby('tmc_code', sort=False).\
    apply(newCrash).reset_index(level=0, drop=True)

对于您的(非常小的)样本数据,此解决方案运行速度较慢。

但是在我将 df_rts_1 的大小增加到 40 行之后,这个解决方案 工作速度更快。

如果再增大df_rts_1的大小,速度上的差别 应该更大(有利于第二种解决方案)。

在样本上检查两种解决方案(原始的和这个)。 100,000 行 从 df_rts_1 写下这两个解决方案的执行时间。