在MATLAB中高效找到三个char向量的Unique三胞胎

myr*_*dio 6 performance matlab vector unique

给定三个字符数组,说size(a) = [N,80]size(b) = [N,100];; size(c) = [N,10];

N=5 ab并且c看起来像,

ans =

  5×80 char array

‘efawefref’
‘Afreafraef’
‘afeafefaef’
‘afeafeaffa’
‘afeafaefae’
Run Code Online (Sandbox Code Playgroud)

我想找到唯一条目(不是组合),这是 x = [a, b, c]

我当然可以做,unique([a, b, c])但是对于这些数据来说这太慢了。N~1e7

例,

   a = [   'timon ';
        'simba ';
        'nala  ';
        'timon ';
        'mufasa'];

b = [   'boar   ';
        'lion   ';
        'lionese';
        'boar   ';
        'lion   '];

c = [   'chubby';
        'small ';
        'fat   ';
        'chubby';
        'fit   '];


unique([a,b,c],'rows')

ans =

  4×19 char array

    'mufasalion   fit   '
    'nala  lionesefat   '
    'simba lion   small '
    'timon boar   chubby'


size(unique([a,b,c],'rows'),1)

ans =

     4
Run Code Online (Sandbox Code Playgroud)

有没有更聪明的方法可以做到这一点?

编辑:答案的结果对于这些大小的条目,

>> size(a)

ans =

    11724952          76

>> size(b)

ans =

    11724952          64

>> size(c)

ans =

    11724952           6
Run Code Online (Sandbox Code Playgroud)

结果

@myradio

>> tic, size(unique(horzcat(a,b,c),'rows')), toc

ans =

     1038303         146

Elapsed time is 74.402044 seconds.
Run Code Online (Sandbox Code Playgroud)

@gnovice 1

>> tic, size(unique(cellstr([a b c]))), toc

ans =

     1038303           1

Elapsed time is 77.044463 seconds.
Run Code Online (Sandbox Code Playgroud)

@gnovice 2

>> tic, map = containers.Map(cellstr([a b c]), ones(length(a), 1)); size(map.keys.'), toc

ans =

     1038303           1

Elapsed time is 58.732947 seconds.
Run Code Online (Sandbox Code Playgroud)

@沃尔菲

>> tic, size(unique( [categorical(cellstr(a)),categorical(cellstr(b)),categorical(cellstr(c))], 'rows' )), toc

ans =

     1038303           3

Elapsed time is 189.517131 seconds.
Run Code Online (Sandbox Code Playgroud)

@obchardon

>> tic, x = primes(2000); a1 = prod(x(a+0),2); b1 = prod(x(b+0),2); c1 = prod(x(c+0),2); size(unique([a1,b1,c1],'rows')), toc

ans =

     1038258           3

Elapsed time is 46.889431 seconds.
Run Code Online (Sandbox Code Playgroud)

我对最后一个感到困惑,我尝试了其他示例,但它总是给出较低的值。

gno*_*ice 4

为了模仿问题中更大的数据集,我使用以下命令创建了以下随机字符数组randi

a = char(randi([65 90], [100 76]));      % Generate 100 76-character arrays
a = a(randi([1 100], [11724952 1]), :);  % Replicate rows: 11724952-by-76 result
b = char(randi([65 90], [100 64]));      % Generate 100 64-character arrays
b = b(randi([1 100], [11724952 1]), :);  % Replicate rows: 11724952-by-64 result
c = char(randi([65 90], [100 6]));       % Generate 100 6-character arrays
c = c(randi([1 100], [11724952 1]), :);  % Replicate rows: 11724952-by-6 result
Run Code Online (Sandbox Code Playgroud)

ab和中每个最多有 100 个唯一字符串c,连接时将产生接近 1,000,000 个唯一组合。

然后我测试了 3 种解决方案:原始的 using unique、一种将字符数组转换为字符串元胞数组的变体 usingcellstr以避免使用'rows'参数,以及一种使用containers.Map对象。最后一个将字符串作为键提供给类containers.Map(具有虚拟关联值),并让它创建一个仅将唯一字符串作为键的映射,然后您可以提取该映射。

由于这些测试至少需要 1 分钟才能运行,因此使用更准确的计时例程timeit(多次运行该函数以获得平均测量值)是不可行的。因此我使用了tic/ toc。以下是使用版本 R2018a 的一些典型结果:

>> clear d
>> tic; d = unique(horzcat(a, b, c), 'rows'); toc
Elapsed time is 726.324408 seconds.

>> clear d
>> tic; d = unique(cellstr([a b c])); toc
Elapsed time is 99.312927 seconds.

>> clear d
>> tic; map = containers.Map(cellstr([a b c]), ones(size(a, 1), 1)); d = map.keys.'; toc
Elapsed time is 89.853430 seconds.
Run Code Online (Sandbox Code Playgroud)

两种更快的解决方案的平均速度通常大致相同,但containers.Map平均速度稍快一些。unique它们都比使用with the argument快得多'rows',尽管这与使用版本 R2018b 的帖子中的结果不一致。也许unique在新版本中进行了重大更新,或者字符数组的具体内容可能非常重要(例如,所有字符串是否以大致相同的频率重复,数组是否已排序与未排序等)。