假设我有一个矩阵A
,其第一列包含一些商品ID,第二列包含0或1。
A=[3 1
1 0
4 0
3 0
1 1
2 1
3 1
4 0
2 0
4 1
3 1
4 0
2 1
1 1
2 0];
我想找到哪个项目ID最多1,并逐个从A中提取其条目。所以,我这样做的方法是,我创建一个矩阵B
从A中提取所有1个条目,在freq_item{1}
中找到最常出现的项目ID B
,然后从该ID的A
中提取所有条目。然后,删除最频繁项目的所有实例,并搜索下一个最常用的项目。如果2个或更多项目具有相同数量的1,则选择比率大于1的项目:
B = A(A(:,2)==1,:);
for i=1:size(unique(A(:,1)),1)
freq_item{i} = A(A(:,1)==mode(B(:,1)),:);
B = B(B(:,1)~=mode(B(:,1)),:);
end
所以,输出是:
freq_item{1,1}=[3 1
3 0
3 1
3 1]
freq_item{1,2}=[1 0
1 1
1 1]
freq_item{1,3}=[2 1
2 0
2 1
2 0]
freq_item{1,4}=[4 0
4 0
4 1
4 0]
但是这段代码需要有引入中间矩阵B
的开销。是否有代码可以在不需要中间矩阵B
的情况下完成此任务,并且至少与上述代码一样快(即,其时间复杂度小于或等于上面编写的代码的时间复杂度)?
答案 0 :(得分:5)
accumarray
的另一项工作:
%// prep
subs = A(:,1);
vals = A(:,2);
%// find id with amximum occurences
[~, id] = max( accumarray(subs,vals) )
%// find indices of that id
idx = find(A == id)
%// filter output
out = A(idx,:)
或更短
[~, id] = max( accumarray(A(:,1),A(:,2)) )
out = A(find(A == id),:)
%// prep
subs = A(:,1);
vals = A(:,2);
%// find id with maximum occurences and mean values
sums = accumarray(subs,vals)
ratios = accumarray(subs,vals,[],@mean)
rows = 1:numel(sums)
%// distributing
unsorted = accumarray(subs,1:numel(subs),[],@(x) {A(x,:)} )
%// sorting indices
[~,idx] = sortrows([sums(:),ratios(:),rows(:)],[-1 -2 3])
sorted = unsorted(idx)
sorted{1,2} =
3 0
3 1
3 1
3 1
sorted{2,2} =
1 0
1 1
1 1
sorted{3,2} =
2 0
2 0
2 1
2 1
sorted{4,2} =
4 1
4 0
4 0
4 0
答案 1 :(得分:1)
首先,找到最常见的ID
mode(A(logical(A(:,2))))
此语句使用逻辑索引仅考虑那些,以及mode
函数来返回最常出现的值。
你也可以这样说:
mode(A( A(:,2) == 1 ))
然后,提取与该值对应的行
A( A(:,1) == mode(A( A(:,2) == 1 )),:)
这里我们将第一列(A(:,1)
)与最常见的ID进行比较,后者返回一个布尔向量。然后使用逻辑索引来提取每个匹配的行和相应的列。
[B,IX] = sort(histc(A( A(:,2) == 1 ),unique(A(:,1))),'descend');
freq={};
for a=IX';
freq{end+1}=A(A(:,1) == subsref(unique(A(:,1)),struct('type','()','subs',{{a}})),:);
end
答案 2 :(得分:1)
这是另一种选择,主要使用histcounts
。很难将其与@MayeulC解决方案进行比较,因为它确实做了更多的事情。至于@thewaywewalk答案,一般情况下,如果A
大于histcounts
might be a better choice而不是accumarray
。但是,这里的答案(使用for
循环)始终更快(并且随着A
更大,它会变得更好)。
这是非中间变量 - 非常慢和丑陋的版本:
output = arrayfun(@(k) A(A(:,1)==k,:),...
subsref(sortrows([...
histcounts(A(logical(A(:,2)),1),min(A(:,1)):max(A(:,1))+1);
histcounts(A(logical(A(:,2)),1),min(A(:,1)):max(A(:,1))+1)./...
histcounts(A(:,1),min(A(:,1)):max(A(:,1))+1);
min(A(:,1)):max(A(:,1))].',[-1 -2]),struct('type','()','subs',{{...
subsref(sortrows([...
histcounts(A(logical(A(:,2)),1),min(A(:,1)):max(A(:,1))+1);
histcounts(A(logical(A(:,2)),1),min(A(:,1)):max(A(:,1))+1)./...
histcounts(A(:,1),min(A(:,1)):max(A(:,1))+1);
min(A(:,1)):max(A(:,1))].',[-1 -2]),struct('type','()','subs',{{':',2}}))>0,3}}))...
,'UniformOutput',false);
这是更具可读性和更快的版本:
ID_range = (min(A(:,1)):max(A(:,1))+1); % the IDs
one_count = histcounts(A(logical(A(:,2)),1),ID_range); % the count of 1's
zero_count = histcounts(A(:,1),ID_range); % the count of 0's
sortedIDs = sortrows([one_count; one_count./zero_count; ID_range(1:end-1)].',[-1 -2]);
output = cell(numel(nonzeros(sortedIDs(:,1))),1);
for k = 1:numel(output)
output{k} = A(A(:,1)==sortedIDs(k,3),:);
end
要测试这个,你需要另一个矩阵,它的ID尚未按照这种方式排序:
A = [3 1;
5 0;
4 0;
3 0;
5 1;
7 1;
3 1;
4 0;
5 0;
4 1;
3 1;
4 0;
7 1;
5 1;
7 0];
我们得到输出:
output{1} =
3 1
3 0
3 1
3 1
output{2} =
7 1
7 1
7 0
output{3} =
5 0
5 1
5 0
5 1
output{4} =
4 0
4 0
4 1
4 0