这是一个关于我们是否可以在matlab中使用矢量化操作类型以避免写入循环的问题。
我有一个载体
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)
之间,我想要返回一个向量包含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]
但我想知道是否有办法避免写入循环? (我看到我的问题有很多非常好的答案。非常感谢你!如果我们更进一步,如果我的Q是一个矩阵,那么我想检查是否)
Y = zeros(length(X),1)
for i = 1:1:length(X)
Y(i) = find(X(i)<Q(i), 1);
end
答案 0 :(得分:8)
使用max
的第二个输出,它作为一种“矢量化find
”:
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
如何运作:
X
的每个元素,请测试它是否小于Q
的每个元素。这是通过bsxfun(@lt, X(:).', Q(:))
完成的。请注意,结果中的每一列对应一个X
元素,每一行对应一个元素Q
。X
的每个元素,获取该比较为Q
的{{1}}的第一个元素的索引。这是通过true
完成的。请注意,[~, Y] = max(..., [], 1)
的第二个输出返回第一个最大化器的索引(沿指定维度),因此在这种情况下,它会给出每个max
的索引。列。对于您的示例值,
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);
答案 1 :(得分:6)
使用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
答案 2 :(得分:4)
Octave有一个函数lookup
来做到这一点。它需要一个有序值和数组的查找表,并返回一个数组,其中包含查找表中值的索引。
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
唯一的问题是您的查找表有一个隐含的零,可以通过以下方式轻松修复:
octave> lookup ([0 Q], X) # alternatively, just add 1 at the results
ans =
2 4 3 4
答案 3 :(得分:3)
您可以创建一个匿名函数来执行比较,然后使用arrayfun
将其应用于X的每个成员:
compareFunc = @(x)find(x < Q, 1);
result = arrayfun(compareFunc, X, 'UniformOutput', 1);
创建匿名函数时,Q数组将存储在匿名函数(compareFunc
)中。
或者,作为一行(Uniform Output是arrayfun的默认行为):
result = arrayfun(@(x)find(x < Q, 1), X);
答案 4 :(得分:2)
如果您拥有的矢量沿着不同的维度,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中使用此示例。
答案 5 :(得分:0)
受@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成为“相同的形状”,然后使用元素比较,然后我们得到一个逻辑矩阵,其行告诉X中的给定元素是否小于Q中的每个元素,然后返回该逻辑矩阵的每一行的第一个非零索引。我没有测试过这种方法与其他方法的比较速度