1

我有一个包含各种数据类型的表。例如,

chars = {'a'; 'bc'; 'de'};
nums = [1; 20; 300];
tbl = table(chars, nums);

我想将所有数值列转换为字符数组变量。通过这个,我的意思是结果应该等同于如果我使用以下方法初始化表:

nums = {'1'; '20', '300'};
tbl = table(chars, nums); 

我想尽可能快地做到这一点,因为我有一个可能有数百万行的表。

目前的工作解决方案:

首先,我得到数字列,在本例中,这将是第 2 列...

% Get the columns numbers which are numerical. This works fine, although a bit clunky.
numcols = find(varfun(@isnumeric, tbl(1,:), 'output', 'uniform'));    
colnames = tbl.Properties.VariableNames(numcols); % Get the corresponding column names

然后,我有几种方法可以将这些数值列转换为字符数组类型,它们都涉及循环数值列并使用其他一些变相的循环___fun函数......

  1. arrayfun

    for ii = 1:numel(colnames)
        % This arrayfun is *slow* for large tables
        tbl.(colnames{ii}) = arrayfun( @num2str, tbl.(colnames{ii}), 'uniformoutput', 0 );
    end
    
  2. num2cellcellfun

    for ii = 1:numel(colnames)
        % num2cell is relatively quick
        tbl.(colnames{ii}) = num2cell( tbl.(colnames{ii}) ); 
        % cellfun is about as slow as arrayfun, as might be expected
        tbl.(colnames{ii}) = cellfun( @num2str, tbl.(colnames{ii}), 'uniformoutput', 0 );
    end
    

下面的速度测试,注意我只做一列和 1e5 个元素,而实际上我想做很多列和可能十倍的行,所以你可以清楚地看到速度问题......

% Setup
nums = (1:1e5).'; tbl = table(nums);
% Functions
f1 = @() arrayfun( @num2str, tbl.nums, 'uni', 0 );
f2 = @() cellfun( @num2str, num2cell(tbl.nums), 'uni', 0 );
% Timing
timeit(f1) % >> 5.15 sec
timeit(f2) % >> 5.16 sec

您可以看到这些方法基本上是等效的,这可能与它们的相似性所预期的一样。

有谁知道将表中的所有数据转换为字符数组变量类型的更快方法?我曾想过通过分类,但不知道如何在那里进行。

我更喜欢与 R2015b 兼容的解决方案。

注意:我的数据包含混合类型是相关的,因为我不能(也不想)varfun( ... )在整个表上使用。

4

1 回答 1

1

您可以使用sprintf转换为大型 char 数组,然后使用将其读回strread以放入单元格数组:

% Setup
nums = (1:1e5).'; tbl = table(nums);
% Functions
f1 = @() arrayfun( @num2str, tbl.nums, 'uni', 0 );
f2 = @() cellfun( @num2str, num2cell(tbl.nums), 'uni', 0 );
f3 = @() strread ( sprintf ( '%i\n', tbl.nums ), '%s', 'delimiter', '\n' );
f4 = @() textscan ( sprintf ( '%i\n', tbl.nums ), '%s', 'delimiter', '\n' );
% Timing
timeit(f1) % 
timeit(f2) % 
timeit(f3) % 
timeit(f4) %


r1 = feval(f1);
r2 = feval(f2);
r3 = feval(f3);
r4 = feval(f4);

% check they are equal
isequal ( r1, r2 )
isequal ( r3, r1 )
isequal ( r4{1}, r1 )

在装有R2015b的计算机上,我得到:

f1 -> 3.78 seconds
f2 -> 3.79 seconds
f3 -> 0.10 seconds
f4 -> 0.07 seconds

根据等量检查,它们都是一样的。

如果您的数据是非整数 -> 您将需要更改 sprintf 语句,我将其设置为整数以确保 isequal 验证结果相同。

于 2017-12-05T12:35:32.307 回答