我有一大组数据(约100万个条目),存储为具有多个列和多个行的单元格。我的问题是我需要识别同时发生的条目,然后操纵其他列,以便删除重复日期的行,而不会丢失所有信息。
此类数据子集的示例可以初始化为;
data = {'10:30', 100; '10:30', 110; '10:31', 115;'10:32', 110}
也就是说,我有一个带有一列字符串(表示时间)的单元格,以及另一列(实际数据中有很多)的双精度单元格。
我的代码应该注意重复的10:30(可能有很多这样的重复),然后能够接受相应的双打(100和110)作为某些函数的输入f(100,110),然后删除从数据中重复一行。
即。如果函数是平均值,那么我应该有一个类似于
的输出data =
'10:30' [105]
'10:31' [115]
'10:32' [110]
如果循环足够快,这将非常简单,但是对于我的数据集,即使尝试涉及循环的解决方案也没有意义。
我已经到了
[uniqueElements, firstUniquePosition, commonSets] = unique(data(:,1));
经过多次摆弄后,产生了一些看似有用的信息,
uniqueElements =
'10:30'
'10:31'
'10:32'
firstUniquePosition =
1
3
4
commonSets =
1
1
2
3
但我无法弄清楚如何制作一个矢量化语句,允许我操纵具有公共日期的元素。
我想在某些时候它会涉及cellfun
,但我还没有足够的matlab功能来实现它,却没有朝着正确的方向前进。
答案 0 :(得分:5)
这是accumarray
的工作:
[times,~,subs] = unique(data(:,1));
idx = 1:size(data,1);
meanOfCommonTimes = accumarray(subs(:),idx(:),[],@(x) mean( [data{x,2}] ))
output = [times num2cell(meanOfCommonTimes)]
output =
'10:30' [105]
'10:31' [115]
'10:32' [110]
谈论100万个元素和性能:考虑使用datenum函数将时间数据存储为数值。
times = datenum(data(:,1),'hh:mm');
并将数据保存在双数组中:
vals = cell2mat(data(:,2));
然后计算最多快10倍!
[~,~, subs] = unique(times);
meanOfCommonTimes = accumarray(subs(:),vals(:),[],@mean);
但请注意。转换也需要相当长的时间。如果你稍后进行了大量的计算,那就可以了。
function [t] = bench()
data = {'10:30', 100; '10:30', 110; '10:31', 115;'10:32', 110};
data = [repmat(data, 200000, 1)]; % I use a matrix rather than a cell array for the simplicity of randomly generating example data
% functions to compare
fcns = {
@() thewaywewalk(data);
@() Cecilia(data);
};
thewayw = timeit(fcns{1})
Ceci = timeit(fcns{2})
end
function Z = Cecilia(data)
[uniqueElements, ~, commonSets] = unique(data(:,1));
num_unique = length(uniqueElements);
Z = zeros(num_unique, 1);
for ii = 1:num_unique
Z(ii) = mean([data{commonSets==ii, 2}]);
end
end
function Z = thewaywewalk(data)
[~,~,subs] = unique(data(:,1));
idx = 1:size(data,1);
Z = accumarray(subs(:),idx(:),[],@(x) mean( [data{x,2}] ));
end
对于具有800000行的数组,结果几乎相等。
thewayw = 1.1483
Ceci = 1.0957
但同样,accumarray
将从之前转换为双倍获益,但在这种情况下,循环的性能应该保持不变。
答案 1 :(得分:1)
循环的执行情况取决于您拥有多少个唯一日期,而不是有多少数据点。如果您的唯一日期数量很少,您可以执行以下操作
data = {'10:30', 100; '10:30', 110; '10:31', 115;'10:32', 110};
[uniqueElements, firstUniquePosition, commonSets] = unique(data(:,1));
num_unique = length(uniqueElements);
mean_of_times = zeros(num_unique, 1);
for ii = 1:num_unique
mean_of_times(ii) = mean([data{commonSets==ii, 2}]);
end
output = [uniqueElements num2cell(mean_of_times)]
output =
'10:30' [105]
'10:31' [115]
'10:32' [110]
那么for循环有多糟糕?我测试了多达20,000个唯一日期,行数为100倍,共计2,000,000行。这是我实验的情节。这可能有点棘手,但accumarray
点都被x轴向下。
这是实验代码
figure; hold on;
kk = 100; %Make 100 times as many rows as dates
for jj = 5000:5000:20000
dates = 1:jj;
times = rand(jj*kk, 1);
% I use a matrix rather than a cell array for the simplicity of randomly generating example data
data = [repmat(dates, 1, kk)' times];
data = data(randperm(jj*kk), :); %Shuffle data rows
[uniqueElements,~,commonSets] = unique(data(:,1));
%thewaywewalk's solution using accumarray
tic;
idx = 1:size(data,1);
accumarray(commonSets(:),idx(:),[],@(x) mean( [data(x,2)] ));
stopwatch = toc;
plot(jj, stopwatch, 'b.');
%my solution using a for loop
tic;
num_unique = length(uniqueElements);
mean_of_times = zeros(num_unique, 1);
for ii = 1:num_unique
mean_of_times(ii) = mean([data(commonSets==ii, 2)]);
end
stopwatch = toc;
plot(jj, stopwatch, 'r.');
end
此实验仅测试与行相比的1%唯一日期。对于更独特的日期,for循环会更慢。 thewaywewalk采用针对具有3个唯一日期的数据集的基准。即使有这么少的类似日期,accumarray
和for循环也有类似的运行时。结论?使用accumarray
。