向量化间隔限制 table 中的查找值
vectorize lookup values in table of interval limits
这里有一个问题,关于我们是否可以在matlab中使用向量化类型的操作来避免写for循环。
我有一个向量
Q = [0.1,0.3,0.6,1.0]
我在 [0,1)
上生成一个均匀分布的随机向量
X = [0.11,0.72,0.32,0.94]
我想知道 X
的每个条目是否在 [0,0.1)
或 [0.1,0.3)
或 [0.3,0.6)
或 [0.6,1.0)
之间,我想 return 一个向量,其中包含 Q
中每个条目小于 X
的最大元素的索引。
我可以写一个 for 循环
Y = zeros(length(X),1)
for i = 1:1:length(X)
Y(i) = find(X(i)<Q, 1);
end
此示例的预期结果:
Y = [2,4,3,4]
但是我想知道有没有办法避免写for循环? (我看到很多很好的回答我的问题。非常感谢!现在如果我们更进一步,如果我的 Q 是一个矩阵,我想检查是否)
Y = zeros(length(X),1)
for i = 1:1:length(X)
Y(i) = find(X(i)<Q(i), 1);
end
您可以创建一个匿名函数来执行比较,然后使用 arrayfun
:
将其应用于 X 的每个成员
compareFunc = @(x)find(x < Q, 1);
result = arrayfun(compareFunc, X, 'UniformOutput', 1);
创建匿名函数时,Q数组将存储在匿名函数(compareFunc
)中。
或者,作为一行(统一输出是 arrayfun 的默认行为):
result = arrayfun(@(x)find(x < Q, 1), X);
使用bsxfun
将有助于实现这一点。你需要阅读它。我还在开头加了一个Q = 0来处理小X的情况
X = [0.11,0.72,0.32,0.94 0.01];
Q = [0.1,0.3,0.6,1.0];
Q_extra = [0 Q];
Diff = bsxfun(@minus,X(:)',Q_extra (:)); %vectorized subtraction
logical_matrix = diff(Diff < 0); %find the transition from neg to positive
[X_categories,~] = find(logical_matrix == true); % get indices
% 输出为 2 4 3 4 1
编辑:每种方法需要多长时间?
我很好奇每个解决方案之间的区别:
下面的测试代码:
Q = [0,0.1,0.3,0.6,1.0];
X = rand(1,1e3);
tic
Y = zeros(length(X),1);
for i = 1:1:length(X)
Y(i) = find(X(i)<Q, 1);
end
toc
tic
result = arrayfun(@(x)find(x < Q, 1), X);
toc
tic
Q = [0 Q];
Diff = bsxfun(@minus,X(:)',Q(:)); %vectorized subtraction
logical_matrix = diff(Diff < 0); %find the transition from neg to positive
[X_categories,~] = find(logical_matrix == true); % get indices
toc
运行 你自己看,我发现当 X 的大小为 1e6 时,bsxfun 快得多,而对于较小的数组,差异是变化的并且可以忽略不计。
示例:当尺寸 X 为 1e3
Elapsed time is 0.001582 seconds. % for loop
Elapsed time is 0.007324 seconds. % anonymous function
Elapsed time is 0.000785 seconds. % bsxfun
Octave 为您提供了一个巧妙的自动矢量化技巧,如果您拥有的矢量沿着不同的维度。如果你让Q
成为一个列向量,你可以这样做:
X = [0.11, 0.72, 0.32, 0.94];
Q = [0.1; 0.3; 0.6; 1.0; 2.0; 3.0];
X <= Q
结果是一个 6x4 矩阵,表示 Q
的每个元素小于 X
的哪些元素。我将 Q
设置为与 X
不同的长度只是为了说明这一点:
0 0 0 0
1 0 0 0
1 0 1 0
1 1 1 1
1 1 1 1
1 1 1 1
回到你原来的例子,你可以做
length(Q) - sum(X <= Q) + 1
获得
2 4 3 4
请注意,我在 Q
的定义中使用了分号而不是逗号。如果你想在定义它之后使它成为一个列向量,那么做这样的事情:
length(Q) - sum(X <= Q') + 1
之所以可行,是因为 Octave 隐式地将 bsxfun
应用于行和列向量上的操作。根据@excaza 的评论,MATLAB 在 R2016b 之前不会这样做,所以在 MATLAB 中你可以这样做:
length(Q) - sum(bsxfun(@le, X, Q)) + 1
您可以在 IDEOne 中试用此示例 here。
Octave 有一个函数 lookup
可以做到这一点。它需要查找 table 排序值和一个数组,以及 returns 一个包含查找中值索引的数组 table.
octave> Q = [0.1 0.3 0.6 1.0];
octave> x = [0.11 0.72 0.32 0.94];
octave> lookup (Q, X)
ans =
1 3 2 3
唯一的问题是您的查找 table 有一个隐含的零,可以很容易地修复:
octave> lookup ([0 Q], X) # alternatively, just add 1 at the results
ans =
2 4 3 4
使用 max
的第二个输出,作为一种 "vectorized find
":
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
这是如何工作的:
- 对于
X
的每个元素,测试它是否小于Q
的每个元素。这是用 bsxfun(@lt, X(:).', Q(:))
完成的。请注意,结果中的每一列对应于 X
的一个元素,每一行对应于 Q
. 的一个元素
- 然后,对于
X
的每个元素,获取 Q
的第一个比较为 true
的元素的索引。这是用 [~, Y] = max(..., [], 1)
完成的。请注意 max
returns 的第二个输出是 first 最大化器的索引(沿着指定的维度),因此在这种情况下它给出了第一个的索引每列 true
。
对于您的示例值,
Q = [0.1, 0.3, 0.6, 1.0];
X = [0.11, 0.72, 0.32, 0.94];
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
给予
Y =
2 4 3 4
受@Mad Physicist 发布的解决方案的启发,这是我的解决方案。
Q = [0.1,0.3,0.6,1.0]
X = [0.11,0.72,0.32,0.94]
Temp = repmat(X',1,4)<repmat(Q,4,1)
[~, ind]= max( Temp~=0, [], 2 );
思路是将X和Q做成"same shape",然后使用元素比较,然后我们得到一个逻辑矩阵,其行告诉X中的给定元素是否小于每个元素在 Q 中,然后 return 这个逻辑矩阵每一行的第一个非零索引。我还没有测试过这种方法与其他方法相比有多快
这里有一个问题,关于我们是否可以在matlab中使用向量化类型的操作来避免写for循环。
我有一个向量
Q = [0.1,0.3,0.6,1.0]
我在 [0,1)
X = [0.11,0.72,0.32,0.94]
我想知道 X
的每个条目是否在 [0,0.1)
或 [0.1,0.3)
或 [0.3,0.6)
或 [0.6,1.0)
之间,我想 return 一个向量,其中包含 Q
中每个条目小于 X
的最大元素的索引。
我可以写一个 for 循环
Y = zeros(length(X),1)
for i = 1:1:length(X)
Y(i) = find(X(i)<Q, 1);
end
此示例的预期结果:
Y = [2,4,3,4]
但是我想知道有没有办法避免写for循环? (我看到很多很好的回答我的问题。非常感谢!现在如果我们更进一步,如果我的 Q 是一个矩阵,我想检查是否)
Y = zeros(length(X),1)
for i = 1:1:length(X)
Y(i) = find(X(i)<Q(i), 1);
end
您可以创建一个匿名函数来执行比较,然后使用 arrayfun
:
compareFunc = @(x)find(x < Q, 1);
result = arrayfun(compareFunc, X, 'UniformOutput', 1);
创建匿名函数时,Q数组将存储在匿名函数(compareFunc
)中。
或者,作为一行(统一输出是 arrayfun 的默认行为):
result = arrayfun(@(x)find(x < Q, 1), X);
使用bsxfun
将有助于实现这一点。你需要阅读它。我还在开头加了一个Q = 0来处理小X的情况
X = [0.11,0.72,0.32,0.94 0.01];
Q = [0.1,0.3,0.6,1.0];
Q_extra = [0 Q];
Diff = bsxfun(@minus,X(:)',Q_extra (:)); %vectorized subtraction
logical_matrix = diff(Diff < 0); %find the transition from neg to positive
[X_categories,~] = find(logical_matrix == true); % get indices
% 输出为 2 4 3 4 1
编辑:每种方法需要多长时间?
我很好奇每个解决方案之间的区别:
下面的测试代码:
Q = [0,0.1,0.3,0.6,1.0];
X = rand(1,1e3);
tic
Y = zeros(length(X),1);
for i = 1:1:length(X)
Y(i) = find(X(i)<Q, 1);
end
toc
tic
result = arrayfun(@(x)find(x < Q, 1), X);
toc
tic
Q = [0 Q];
Diff = bsxfun(@minus,X(:)',Q(:)); %vectorized subtraction
logical_matrix = diff(Diff < 0); %find the transition from neg to positive
[X_categories,~] = find(logical_matrix == true); % get indices
toc
运行 你自己看,我发现当 X 的大小为 1e6 时,bsxfun 快得多,而对于较小的数组,差异是变化的并且可以忽略不计。
示例:当尺寸 X 为 1e3
Elapsed time is 0.001582 seconds. % for loop
Elapsed time is 0.007324 seconds. % anonymous function
Elapsed time is 0.000785 seconds. % bsxfun
Octave 为您提供了一个巧妙的自动矢量化技巧,如果您拥有的矢量沿着不同的维度。如果你让Q
成为一个列向量,你可以这样做:
X = [0.11, 0.72, 0.32, 0.94];
Q = [0.1; 0.3; 0.6; 1.0; 2.0; 3.0];
X <= Q
结果是一个 6x4 矩阵,表示 Q
的每个元素小于 X
的哪些元素。我将 Q
设置为与 X
不同的长度只是为了说明这一点:
0 0 0 0
1 0 0 0
1 0 1 0
1 1 1 1
1 1 1 1
1 1 1 1
回到你原来的例子,你可以做
length(Q) - sum(X <= Q) + 1
获得
2 4 3 4
请注意,我在 Q
的定义中使用了分号而不是逗号。如果你想在定义它之后使它成为一个列向量,那么做这样的事情:
length(Q) - sum(X <= Q') + 1
之所以可行,是因为 Octave 隐式地将 bsxfun
应用于行和列向量上的操作。根据@excaza 的评论,MATLAB 在 R2016b 之前不会这样做,所以在 MATLAB 中你可以这样做:
length(Q) - sum(bsxfun(@le, X, Q)) + 1
您可以在 IDEOne 中试用此示例 here。
Octave 有一个函数 lookup
可以做到这一点。它需要查找 table 排序值和一个数组,以及 returns 一个包含查找中值索引的数组 table.
octave> Q = [0.1 0.3 0.6 1.0];
octave> x = [0.11 0.72 0.32 0.94];
octave> lookup (Q, X)
ans =
1 3 2 3
唯一的问题是您的查找 table 有一个隐含的零,可以很容易地修复:
octave> lookup ([0 Q], X) # alternatively, just add 1 at the results
ans =
2 4 3 4
使用 max
的第二个输出,作为一种 "vectorized find
":
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
这是如何工作的:
- 对于
X
的每个元素,测试它是否小于Q
的每个元素。这是用bsxfun(@lt, X(:).', Q(:))
完成的。请注意,结果中的每一列对应于X
的一个元素,每一行对应于Q
. 的一个元素
- 然后,对于
X
的每个元素,获取Q
的第一个比较为true
的元素的索引。这是用[~, Y] = max(..., [], 1)
完成的。请注意max
returns 的第二个输出是 first 最大化器的索引(沿着指定的维度),因此在这种情况下它给出了第一个的索引每列true
。
对于您的示例值,
Q = [0.1, 0.3, 0.6, 1.0];
X = [0.11, 0.72, 0.32, 0.94];
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
给予
Y =
2 4 3 4
受@Mad Physicist 发布的解决方案的启发,这是我的解决方案。
Q = [0.1,0.3,0.6,1.0]
X = [0.11,0.72,0.32,0.94]
Temp = repmat(X',1,4)<repmat(Q,4,1)
[~, ind]= max( Temp~=0, [], 2 );
思路是将X和Q做成"same shape",然后使用元素比较,然后我们得到一个逻辑矩阵,其行告诉X中的给定元素是否小于每个元素在 Q 中,然后 return 这个逻辑矩阵每一行的第一个非零索引。我还没有测试过这种方法与其他方法相比有多快