我有一个包含以下格式信息的文本文件:
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
有人可以告诉我安排信息的最佳方式吗?谢谢,非常感谢帮助。
答案 0 :(得分: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]
现在,现在更方便地存储数据。我可以想到两种方式:
对于两者,您必须首先找到唯一的名称:
[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)
表示a
的第3个元素,不是第4个元素。
命令细分:
函数arrayfun
遍历输入数组的元素,将函数应用于每个元素,并以常规数组(如果可能)或单元格数组合(如果不可能) (错误)并在给出'uniformoutput', false
时)。它有点像foreach
- 构造。
将输入数组等于第一步中找到的唯一names
,诀窍在于应用于每个名称的函数。函数@(x) data(strcmp(tmp{1},x),:)
首先使用tmp{1}
在strcmp
(包含所有名称的数组)中查找给定名称的索引。然后使用这些索引来索引data = [tmp{2:end}]
,即所有其他数组。
然后将每个唯一名称的结果存储在单元格数组results
中。
您可以更进一步,使用单元格数组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_struc
中名为someString
的字段。
现在,如果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
最后一个感谢these guys。
答案 1 :(得分:1)
首先,你一定要使用Rody建议的方法读取数据(Rody为+1指出它),所以我假设你已经到了那么远,并且有一个名为tmp的变量就像在Rody的代码中一样示例
现在,如果我正确理解了问题,您需要能够将示例数据集的每一行与其他行区分开来(使用日期?),但同时您还需要轻松区分不同的名称,一些其中几行相同(再次,我从你的示例数据集中得到这个)。
接近这个的一种可能方式(确实有一个缺点)是使用结构。我将假设你在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
上运行我在上面提供的代码。然后在matlab变量编辑器中查看名为Struct
的结果结构。这应该让您了解它们的工作原理。