【问题标题】:MATLAB: Using textscan and converting cell array in matrixMATLAB:使用 textscan 并在矩阵中转换元胞数组
【发布时间】:2013-07-15 12:38:08
【问题描述】:

我有一个大的 csv 文件(大约 100 万行),其中包含具有以下结构的选项数据(内容已更改):

secid, date, days, delta, impl_volatility, impl_strike, impl_premium, dispersion, cp_flag, ticker, index_flag, industry_group
100000, 02/05/1986, 60, -80, 0.270556, 74.2511, 5.2415, 0.021514, C, ASC, 0, 481
100000, 03/05/1986, 30, -40, 0.251556, 74.2571, 6.2415, 0.025524, P, ASC, 0, 481

我已经使用以下方法成功导入了一个测试文件:

ftest = fopen('test.csv');
C = textscan(ftest,'%f %s %f %f %f %f %f %f %s %s %f %f','Headerlines',1,'Delimiter',',');
fclose(ftest);

但是,C 是一个元胞数组,这使得在 matlab 中处理文件的内容变得更加困难。将它作为“常规”数组会更容易(请原谅我不知道正确的命名法,我刚开始使用 matlab)。

如果我输出 C,我得到:

Columns 1 through 6
[2x1 double]    {2x1 cell}    [2x1 double]    [2x1 double]    [2x1 double]    [2x1 double]
Columns 7 through 12
[2x1 double]    [2x1 double]    {2x1 cell}    {2x1 cell}    [2x1 double]    [2x1 double]

所以在 C 的元胞数组中,有数组和元胞数组 - 用于数字的数组和用于字符串的元胞数组。如果我尝试检查元素 (1,2),我必须使用 C{1}(2),但如果我想检查元素 (2,2),我必须使用 C{2}{2}。理想情况下,我想同时访问 C(1,2) 和 C(2,2)。 问题是,我该怎么做?

我已经搜索了解决方案并找到了 cells2mat,但它只有在所有内容都是数字的情况下才有效(我认为)。我找到了这个解决方案:Convert cell array of cell arrays to matrix of matrices,但是 horzcat 检索到一个错误,我相信这可能是由于同样的问题而发生的。

提前感谢您的宝贵时间。

【问题讨论】:

  • 元胞数组是可以存储不同类型值的数组。在这里,您有一个存储数字和字符串列的元胞数组。 This question 假设您所拥有的只是数值,因此元胞数组确实是多余的,并且它可以转换为矩阵,这更容易操作。但是,当您涉及字符串时,您期望得到什么?

标签: arrays matlab csv cell textscan


【解决方案1】:

因为您有一个包含数字和字符数据的数组,所以您想要的是不可能的(相信我,这也是不切实际的)。

引用数字数组中的单个数字不同于引用整个字符串。根本无法逃避,也不应该存在:您对待鲜花的方式与对待人的方式不同(我当然希望如此)。

在 MATLAB 中,字符串是一个普通的数组,区别在于数组的每个条目表示的不是一个数字,而是一个字符。引用单个字符与引用数组中的数字相同:

>> a = 'my string'
>> a(4)
ans = 
s
>> a+0  % cast to double to show the "true character" of strings
ans =
    109   121    32   115   116   114   105   110   103

但是,textscan 假定(正确地)您不想这样做,而是想从文件中提取整个字符串。并且应该以不同的方式引用整个字符串,以表明您的意思是整个字符串而不是单个字符。

如果您将 textscan 的结果拆分为一个普通的数值数组和一个字符串元胞数组,我想您会发现它更直观一些,如下所示:

% load the data
ftest = fopen('test.csv');
C = textscan(ftest,...
    '%f %s %f %f %f %f %f %f %s %s %f %f',...     
    'collectoutput', true,...
    'Delimiter',',\n');
fclose(ftest);

% split into numeric and char arrays
numeric = [C{[1 3 5]}]
alpha   = [C{[2 4]}]

引用numeric 中的数据遵循与任何普通数组相同的规则,引用alpha 中的字符串则遵循正常的单元格引用规则(如在alpha{2,1} 中获取'03/05/1986'

编辑基于没有你的 cmets,你想像这样进行转换:

% Read the data
fid = fopen('test.csv', 'r');
C = textscan(fid,...
    '%f %s %f %f %f %f %f %f %s %s %f %f',...
    'Headerlines', 1,...
    'Delimiter',',');
fclose(fid);

% Delete 10th element ('ASC')
C(10) = [];

% Mass-convert dates to datenums
C{2} = datenum(C{2}, 'dd/mm/yyyy');

% Map 'P' to 1 and 'C' to 2
map('PC') = [1 2];
C{9} = map([C{9}{:}]).';

% Convert whole array to numeric array
C = [C{:}];

【讨论】:

  • 感谢您的回答@Rody。我明白这个问题。我正在考虑将日期和 cp_flag 转换为数字并忽略ticker 字段。你能告诉我这是否是一种有效的方法吗? ftest = fopen('testfile2.csv'); C = textscan(ftest,'%f %s %f %f %f %f %f %f %s %s %f %f','Headerlines',1,'Delimiter',','); fclose(ftest); for i=1:size(C{9}) C{2}{i} = datenum(C{2}{i}, 'dd/mm/yyyy'); if C{9}{i} == 'P' C{9}{i} = 1; elseif C{9}{i} == 'C' C{9}{i} = 2; end end C{2} = cell2mat(C{2}); C{9} = cell2mat(C{9}); C = [C{[1 2 3 4 5 6 7 8 9 11 12]}]
  • @japa:虽然你的方法没有错,但我编辑的版本更短、更快、更 MATLAB :)
  • 非常感谢@Rody,这样很快。它在大约 10 秒内处理 1052480 行文件。非常感谢!
【解决方案2】:

我遇到了同样的问题...我更喜欢二维元胞数组,以便于访问并利用内置的 matlab 排序功能。

这是另一种可能适合您的解决方案(这就是 TMW 在导入工具中自动生成的代码中的做法)。它将数值数组转换为元胞数组,以便您可以将它们连接成二维矩阵。

C([1,3,4,5,6,7,8,11,12]) = cellfun(@(x) num2cell(x), C([1,3,4,5,6,7,8,11,12]),'UniformOutput', false);

C = [C{1:end}];

【讨论】: