从结构数组中删除“行”

时间:2012-12-14 10:29:15

标签: performance matlab for-loop runtime-error

这类似于我之前提出的问题,但略有不同:

所以我在matlab中有一个非常大的结构数组。假设为了论证,为了简化情况,假设我有类似的东西:

结构(1).name,structure(2).name,structure(3).name structure(1).returns,structure(2).returns,structure(3).returns(在我的真实程序中我有647结构)

进一步假设结构(i).returns是一个向量(非常大的向量,大约2,000,000个条目),并且条件出现在我想从结构(i)中删除第j个条目的地方。所有i的返回。你怎么做到这一点?或者更确切地说,你如何快速地做到这一点?我尝试了一些东西,但它们都非常慢(我会在一秒钟内展示它们)所以我想知道社区是否知道更快的方法来做到这一点。

我以两种不同的方式解析了我的数据;第一种方法是将所有内容保存为单元格数组,但由于事情对我来说效果不佳,我再次解析数据并将所有内容都放在矢量中。

我实际上在做的是尝试删除NaN数据,以及我的数据文件的同一对应行中的所有数据,然后在应用Hampel过滤器后执行相同的操作。我尝试此代码的相关部分是:

for i=numStock+1:-1:1
    for j=length(stock(i).return):-1:1
        if(isnan(stock(i).return(j)))
            for k=numStock+1:-1:1
                stock(k).return(j) = [];
            end
        end
    end
    stock(i).return = sort(stock(i).return);
    stock(i).returnLength = length(stock(i).return);
    stock(i).medianReturn = median(stock(i).return);
    stock(i).madReturn = mad(stock(i).return,1);
end;

for i=numStock:-1:1
    for j = length(stock(i+1).volume):-1:1
        if(isnan(stock(i+1).volume(j)))
            for k=numStock:-1:1
               stock(k+1).volume(j) = [];
            end
        end
    end
    stock(i+1).volume = sort(stock(i+1).volume);
    stock(i+1).volumeLength = length(stock(i+1).volume);
    stock(i+1).medianVolume = median(stock(i+1).volume);
    stock(i+1).madVolume = mad(stock(i+1).volume,1);
end;



for i=numStock+1:-1:1
    for j=stock(i).returnLength:-1:1
        if (abs(stock(i).return(j) - stock(i).medianReturn) > 3*stock(i).madReturn)
            for k=numStock+1:-1:1
                stock(k).return(j) = [];
            end
        end;
    end;
end;

for i=numStock:-1:1
    for j=stock(i+1).volumeLength:-1:1
        if (abs(stock(i+1).volume(j) - stock(i+1).medianVolume) > 3*stock(i+1).madVolume)
            for k=numStock:-1:1
                stock(k+1).volume(j) = [];
            end
        end;
    end;
end;

但是,这会返回错误:

“矩阵索引超出删除范围。

失败错误(第110行)                 股票(k).return(j)= [];“

所以我尝试通过解析所有内容作为向量。然后我决定在构建结构数组之前尝试删除向量中的相应条目。这不会返回错误,但速度很慢:

%% Delete bad data, Hampel Filter

% Delete bad entries
id=strcmp(returns,'');
returns(id)=[];
volume(id)=[];
date(id)=[];
ticker(id)=[];
name(id)=[];
permno(id)=[];
sp500(id) = [];

id=strcmp(returns,'C');
returns(id)=[];
volume(id)=[];
date(id)=[];
ticker(id)=[];
name(id)=[];
permno(id)=[];
sp500(id) = [];

% Convert returns from string to double
returns=cellfun(@str2double,returns);
sp500=cellfun(@str2double,sp500);

% Delete all data for which a return is not a number
nanid=isnan(returns);
returns(nanid)=[];
volume(nanid)=[];
date(nanid)=[];
ticker(nanid)=[];
name(nanid)=[];
permno(nanid)=[];

% Delete all data for which a volume is not a number
nanid=isnan(volume);
returns(nanid)=[];
volume(nanid)=[];
date(nanid)=[];
ticker(nanid)=[];
name(nanid)=[];
permno(nanid)=[];

% Apply the Hampel filter, and delete all data corresponding to
% observations deleted by the filter.

medianReturn = median(returns);
madReturn = mad(returns,1);

for i=length(returns):-1:1
    if (abs(returns(i) - medianReturn) > 3*madReturn)
        returns(i) = [];
        volume(i)=[];
        date(i)=[];
        ticker(i)=[];
        name(i)=[];
        permno(i)=[];
    end;
end

medianVolume = median(volume);
madVolume = mad(volume,1);

for i=length(volume):-1:1
    if (abs(volume(i) - medianVolume) > 3*madVolume)
        returns(i) = [];
        volume(i)=[];
        date(i)=[];
        ticker(i)=[];
        name(i)=[];
        permno(i)=[];
    end;
end

正如我所说,这非常慢,可能是因为我在非常大的数据集上使用for循环;但是,我不确定如何做到这一点。对于巨大的帖子感到抱歉,但有没有人建议我如何以合理的方式做我正在问的事情?

编辑:我应该补充一点,让矢量方法起作用可能更可取,因为我的目标是将所有的返回向量放入一个矩阵中,并将所有的体积向量放入一个矩阵中并对它们执行PCA,我不确定如何使用单元格数组(或者即使princomp可以在单元格数组上工作)。

EDIT2:我已经改变了代码以符合你的建议(虽然我决定放弃速度并保持for-loops以保持结构数组,因为重新分析这些数据会更加节省时间)。新代码snipet是:

stock_return = zeros(numStock+1,length(stock(1).return));

for i=1:numStock+1
    for j=1:length(stock(i).return)
        stock_return(i,j) = stock(i).return(j);
    end
end

stock_return = stock_return(~any(isnan(stock_return)), : );

这会返回索引超出矩阵维度的错误,我不知道为什么。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

我找不到一种方便的方法来处理结构,因此我会重新构造代码,以便它只使用数组而不是结构。
例如,我代替stock(i).return(j) stock_returns(i,j)

我告诉你代码的一部分如何摆脱for循环。

说我们处理这段代码:

for j=length(stock(i).return):-1:1
    if(isnan(stock(i).return(j)))
        for k=numStock+1:-1:1
            stock(k).return(j) = [];
        end
    end
end

现在,删除包含任何NaN数据的列如下:

stock_return = stock_return(:, ~any(isnan(stock_return)) );

至于与medianVolume的绝对差异,您可以编写类似的代码:

% stock_return_length is a scalar
% stock_median_return is a column vector (eg. [1;2;3])
% stock_mad_return is also a column vector.

median_return = repmat(stock_median_return, stock_return_length, 1);
is_bad = abs(stock_return - median_return) > 3.* stock_mad_return;
stock_return = stock_return(:, ~any(is_bad));

使用stock_return_length的标量当然意味着返回长度相同,但无论如何你隐含地假设它在原始代码中。

我的回答中的重点是使用any。逻辑索引本身是不够的,因为在原始代码中,如果任何值都不好,则删除所有值。

参考任何:http://www.mathworks.co.uk/help/matlab/ref/any.html


如果你想保留原始结构,那么你坚持使用stock(i).return,你可以使用基本相同的方案来加速你的代码,但是你只能删除少一个for循环,这意味着你的程序会慢得多。