4

我有一个具有以下信息格式的文本文件:

Name1 34 25 36 46
Name1 23 53 15 86
Name1 25 25 87 35
Name2 76 22 44 55
Name2 88 88 88 88
Name3 11 11 11 11
Name3 55 66 88 88
Name3 88 88 88 88
Name3 00 00 00 00

有不同的“名称”,我必须将每个名称排列到一个数组槽中。然后,我需要另一种方法来将与每一行关联的日期分配到该特定位置。例如,第一个 Name1 可能有数组 {0},但我还需要以某种方式关联 34、24、36 和 46。我还需要区分不同的名称。做这个的最好方式是什么?2x2 阵列似乎不是解决方案。

到目前为止,我所拥有的是这样的:

%# read the whole file to a temporary cell array
fid = fopen(filename,'rt');
tmp = textscan(fid,'%s','Delimiter','\n');
fclose(fid);

%# remove the lines starting with headerline
tmp = tmp{1};
idx = cellfun(@(x) strcmp(x(1:10),'headerline'), tmp);
tmp(idx) = [];

%# split and concatenate the rest
result = regexp(tmp,' ','split');
result = cat(1,result{:});

%# delete temporary array (if you want)
clear tmp

礼貌:在 Matlab 中读取 txt 文件

有人可以告诉我安排信息的最佳方式吗?谢谢,非常感谢您的帮助。

4

2 回答 2

5

从代码来看,你为什么不使用

fid = fopen(filename,'rt');
tmp = textscan(fid, '%s %d %d %d %d', 'Headerlines', 10);
fclose(fid);

textscan默认使用空格和换行符作为分隔符。如果显式地将换行符作为分隔符,则会失去作为分隔符的空格和可移植性(Windows 通常\r\n用作单个换行符,而 Unix 派生的操作系统使用\n)。因此,鉴于您的数据,请忽略它。

然后你跳过箍来删除 10 个标题,同时textscan已经有一个很好的烘焙选项。因此,不需要这些步骤。您继续通过regexp以空格作为分隔符的传递来拆分内容,但由于textscan已经在空间上拆分,因此也不需要。

所以,使用上面的三行,你会得到

tmp = 
    {9x1 cell}    [9x1 int32]    [9x1 int32]    [9x1 int32]    [9x1 int32]

现在,现在更方便地存储数据。我可以想到两种方法:

  1. 元胞数组
  2. 结构

对于这两者,您必须先找到唯一的名称:

[names, inds] = unique(tmp{1});

使用元胞数组

这将为您提供按名称排序的数据的元胞数组:

data = [tmp{2:end}];
results = arrayfun(@(x) data(strcmp(tmp{1},x),:), ...
            names, 'uniformoutput', false);

现在你可以索引results如下:

results{3}(1,4)   %# for the 4th '11' for 'Name3' 

请记住,Matlab 是基于 1 的,因此a(3)表示 的第 3 个元素a而不是第 4 个元素。

命令分解:

  1. 该函数arrayfun遍历输入数组的元素,对每个元素应用一个函数,并将结果收集到常规数组(如果可能)或元胞数组(当不可能(错误)和给定时'uniformoutput', false)中。这有点像foreach- 构造。

  2. 使输入数组等于names第一步中找到的唯一值,诀窍在于应用于每个名称的函数。该函数首先使用 . 在(包含所有名称的数组)中@(x) data(strcmp(tmp{1},x),:)查找给定名称的索引。这些索引然后用于索引,即所有其他数组。tmp{1}strcmpdata = [tmp{2:end}]

  3. 然后将每个唯一名称的结果存储在 cell-arrayresults中。

使用结构

您可以更进一步,使用元胞数组results来获得更易于人类阅读的数据结构。应用所有前面的步骤后,执行以下操作:

for ii = 1:numel(names)
    output.(names{ii}) = results{ii}; end

现在您可以按名称引用您的数据:

output.Name3(1,4)   %# to index the 4th '11' from 'Name3'

该语法your_struct.('someString')称为动态结构引用。它在名为的结构中引用或创建一个字段。your_strucsomeString

现在,如果names{ii}包含你想摆脱的下划线,那么你可以定义

camelCase = @(x) regexprep(x, '_+(\w?)', '${upper($1)}')

或者

camelCase = @(x) regexprep(x, ' +(\w?)', '${upper($1)}')

对于空间。然后使用

for ii = 1:numel(names)
    output.( camelCase(names{ii}) ) = results{ii}; end

最后一个感谢这些家伙

于 2012-09-18T05:03:33.080 回答
1

首先,您绝对应该使用 Rody 建议的方法读取数据(Rody 指出时 +1),所以我假设您已经做到了这一点,并且有一个名为 tmp 的变量,就像在 Rody 的代码示例中一样。

现在,如果我正确理解了这个问题,您需要能够将示例数据集的每一行与其他行区分开来(使用日期?),但同时您还需要轻松区分不同的名称,其中一些名称将在几行中是相同的(同样,我从您的示例数据集中得到这个)。

解决这个问题的一种可能方法(确实有一个缺点)是使用结构。我将假设您已经在罗迪的回答中获得了变量 tmp ,我们将从那里开始。使用代码:

NameVec = unique(tmp{1, 1});
for i = 1:1:size(NameVec, 1)
    Index = ismember(tmp{1, 1}, NameVec{i, 1});
    Struct.(NameVec{i, 1}).Data = ...
       [tmp{1, 2}(Index), tmp{1, 3}(Index), tmp{1, 4}(Index), tmp{1, 5}(Index)];
end
Struct.NameVec = NameVec;

此代码将创建一个结构,其中结构中的第一级具有数据集中每个唯一名称的字段名称(我还在代码中包含NameVec结构第一级中的变量,因此它可用于引用后面有一个循环的各个字段)。然后在每个字段(本例中的 Name1、Name2 和 Name3)中,我保存了一个数据矩阵,其中包含与该名称关联的数据(其中保留了各个行)。

这种方法的缺点是,如果要将所有数据返回到一个大数组中,则需要遍历 Struct.NameVec 的元素并检索与每个唯一名称关联的数据矩阵。并且循环在matlab中很慢。所以说真的,这取决于你打算如何使用这些数据。

希望这可以帮助!

ps,如果你不熟悉matlab结构,运行这段代码:

tmp = cell(1, 5);
tmp{1, 1} = {'Name1'; 'Name1'; 'Name1'; 'Name2'; 'Name2'; 'Name3'; ...
'Name3'; 'Name3'; 'Name3';};
tmp{1, 2} = [34;23;25;76;88;11;55;88;00];
tmp{1, 3} = [25;53;25;22;88;11;66;88;00];
tmp{1, 4} = [36;15;87;44;88;11;88;88;00];
tmp{1, 5} = [46;86;35;55;88;11;88;88;00];

然后运行我上面提供的代码tmp。然后看看在Structmatlab 变量编辑器中调用的结果结构。这应该让您对它们的工作方式有所了解。

于 2012-09-18T09:08:36.817 回答