需要 Numpy 帮助:如何使用布尔值来计算范围并在范围内将值相加?



我有一个Nx2矩阵,例如:

M = [[10, 1000],
[11, 200],
[15, 800],
[20, 5000],
[28, 100],
[32, 3000],
[35, 3500],
[38, 100],
[50, 5000],
[51, 100],
[55, 2000],
[58, 3000],
[66, 4000],
[90, 5000]]

我需要创建一个 Nx3 矩阵,它通过以下方式反映第一个矩阵中的行之间的关系:

使用右列标识范围边界的候选项,条件为值>= 1000

此条件适用于矩阵:

[[10, 1000],
[20, 5000],
[32, 3000],
[35, 3500],
[50, 5000],
[55, 2000],
[58, 3000],
[66, 4000],
[90, 5000],]

到目前为止,我想出了"M[M[:,1]>=1000]",它有效。对于这个新矩阵,我现在要检查第一列中到下一个点 <= 10 的距离适用的点,并将其用作范围边界。

到目前为止我想出了什么:np.diff(M[:,0]( <= 10,返回:

[True, False, True, False, True, True, True, False]

这就是我卡住的地方。我想使用此条件来定义范围的下边界和上限。例如:

[[10, 1000], #<- Range 1 start
[20, 5000], #<- Range 1 end (as 32 would be 12 points away)
[32, 3000], #<- Range 2 start
[35, 3500], #<- Range 2 end
[50, 5000], #<- Range 3 start
[55, 2000], #<- Range 3 cont (as 55 is only 5 points away)
[58, 3000], #<- Range 3 cont
[66, 4000], #<- Range 3 end
[90, 5000]] #<- Range 4 start and end (as there is no point +-10)

最后,回到第一个矩阵,我想将边界内(包括(每个范围的右列值相加。

所以我有四个范围来定义边界的开始和停止。

  • 范围 1:开始 10,结束 20
  • 范围 2:开始 32,结束 35
  • 范围 3:开始 50,结束 66
  • 范围 4:开始 90,结束 90

生成的矩阵如下所示,其中列 0 是起始边界,第 1 列是结束边界,第 2 列是来自右列的矩阵 M 的加值,位于开始和结束之间。

[[10, 20, 7000], # 7000 = 1000+200+800+5000
[32, 35, 6500], # 6500 = 3000+3500
[50, 66, 14100], # 14100 = 5000+100+2000+3000+4000
[90, 90, 5000]] # 5000 = just 5000 as upper=lower boundary

在获得范围边界的真/假值后,我卡在了第二步。但是我不清楚如何从布尔值创建范围,然后如何在这些范围内将值相加。任何建议将不胜感激。另外,我不确定我的方法,也许有更好的方法可以从第一个矩阵到最后一个矩阵,也许跳过一步??

编辑

因此,我在中间步骤中走得更远,现在可以返回范围的开始值和结束值:

start_diffs = np.diff(M[:,0]) > 10
start_indexes = np.insert(start_diffs, 0, True)
end_diffs = np.diff(M[:,0]) > 10
end_indexes = np.insert(end_diffs, -1, True)
start_values = M[:,0][start_indexes]
end_values = M[:,0][end_indexes]
print(np.array([start_values, end_values]).T)

返回:

[[10 20]
[32 35]
[50 66]
[90 90]]

缺少的是现在以某种方式使用这些范围来计算右列中矩阵 M 的总和。

如果您愿意使用pandas,这里有一个回想起来似乎有点考虑过度的解决方案,但有效:

# Initial array
M = np.array([[10, 1000],
[11, 200],
[15, 800],
[20, 5000],
[28, 100],
[32, 3000],
[35, 3500],
[38, 100],
[50, 5000],
[51, 100],
[55, 2000],
[58, 3000],
[66, 4000],
[90, 5000]])
# Build a DataFrame with default integer index and column labels
df = pd.DataFrame(M)
# Get a subset of rows that represent potential interval edges
subset = df[df[1] >= 1000].copy()
# If a row is the first row in a new range, flag it with 1.
# Then cumulatively sum these 1s. This labels each row with a 
# unique integer, one per range
subset[2] = (subset[0].diff() > 10).astype(int).cumsum()
# Get the start and end values of each range
edges = subset.groupby(2).agg({0: ['first', 'last']})
edges
0     
first last
2           
0    10   20
1    32   35
2    50   66
3    90   90
# Build a pandas IntervalIndex out of these interval edges
tups = list(edges.itertuples(index=False, name=None))
idx = pd.IntervalIndex.from_tuples(tups, closed='both')
# Build a Series that maps each interval to a unique range number
mapping = pd.Series(range(len(idx)), index=idx)
# Apply this mapping to create a new column of the original df
df[2] = [mapping.loc[i] if idx.contains(i) else None for i in df[0]]
df
0     1    2
0   10  1000  0.0
1   11   200  0.0
2   15   800  0.0
3   20  5000  0.0
4   28   100  NaN
5   32  3000  1.0
6   35  3500  1.0
7   38   100  NaN
8   50  5000  2.0
9   51   100  2.0
10  55  2000  2.0
11  58  3000  2.0
12  66  4000  2.0
13  90  5000  3.0
# Group by this new column, get edges of each interval, 
# sum values, and get the underlying numpy array
df.groupby(2).agg({0: ['first', 'last'], 1: 'sum'}).values
array([[   10,    20,  7000],
[   32,    35,  6500],
[   50,    66, 14100],
[   90,    90,  5000]])

最新更新