Arrays 从范畴数组透视到二进制矩阵

Arrays 从范畴数组透视到二进制矩阵,arrays,matlab,performance,for-loop,vectorization,Arrays,Matlab,Performance,For Loop,Vectorization,我有一个数组,其中的一些值属于一个集合。我想在一个二进制矩阵中转换这个数组,这个矩阵的每一列将代表集合中的每一个可能值,行值对于匹配输入数组的列是1,或者对于所有其他列是0。我认为这是一个类似于二进制轴心的名字 输入数组是表类型的列 输入数组示例(前面的示例仅为大写字母,这导致误解): “苹果” “香蕉” “樱桃” “龙果” “苹果” “樱桃” 因此,在这个示例中,输入可以假设4个不同的值:“苹果”、“香蕉”、“樱桃”或“龙果”,在我的真实场景中,它可以超过4个。 输出矩阵示例: 1 0 0 0

我有一个数组,其中的一些值属于一个集合。我想在一个二进制矩阵中转换这个数组,这个矩阵的每一列将代表集合中的每一个可能值,行值对于匹配输入数组的列是1,或者对于所有其他列是0。我认为这是一个类似于二进制轴心的名字

输入数组是表类型的列

输入数组示例(前面的示例仅为大写字母,这导致误解):

“苹果”
“香蕉”
“樱桃”
“龙果”
“苹果”
“樱桃”

因此,在这个示例中,输入可以假设4个不同的值:“苹果”、“香蕉”、“樱桃”或“龙果”,在我的真实场景中,它可以超过4个。

输出矩阵示例:

1 0 0 0
01 0 0
01 0
01
1 0 0 0
01 0

我已经实现了预期的行为,但我想知道是否有更好的方法来执行此操作。以矢量化方式(每个类别不使用for循环)或使用内置函数

 function [ binMatrix, categs ] = pivotToBinaryMatrix( input )
      categorizedInput = categorical(input);

      categs = categories(categorizedInput);

      binMatrix = zeros(size(atributo, 1), size(categorias, 1));

      for i = 1: size(caters,1)
           binMatrix(:,i) = ismember(categorizedInput, categs(i));
      end
 end
在0.075137秒的时间内完成了9个类别的大约50000个条目


编辑:我改进了示例,因为前面的示例导致了误解。

我假设您的输入数组是一个由字符组成的单元格数组,如下所示:

inputArray = {'Apple', 'Banana', 'Cherry', 'Dragonfruit', 'Apple', 'Cherry'};
您可以使用函数的第三个输出将上述内容转换为数字数组。这一点的好处在于,
unique
按排序的顺序分配一个唯一的ID,因此,如果您有一个字符的单元格数组,它将遵循字符的字典顺序

接下来,声明一个由零组成的矩阵(就像上面所做的那样),然后使用索引到矩阵中,并将值设置为1

像这样的。请记住,我初始化的输出略有不同。这是我学会的分配一个非常快的零矩阵的技巧。请看这里:

另一种方法是创建一个逻辑数组,在该数组中,我们将正确的行和列位置设置为1,然后使用它索引到零数组中,并相应地设置值

比如:

inputArray = {'Apple', 'Banana', 'Cherry', 'Dragonfruit', 'Apple', 'Cherry'};
[~,~,inputNum] = unique(inputArray);
inputNum = inputNum.'; %// To make compatible in dimensions
binMatrix = sparse(1:numel(inputArray), inputNum, 1, numel(inputArray), max(inputNum));
binMatrix = full(binMatrix);
让我们把这些放在一个计时脚本中。我已经结合了上面两种方法,再加上你的旧方法,再加上Divakar的(只有第一种方法)和brodroll的(非常巧妙的btw)方法。对于Divakar和brodroll的方法,我还使用了
unique
和第三个输出,因为您最初的查询使用了大写字母,这与所有查询一样混淆。使用第三个输出可以轻松地将以前的方法转换为新的规范

顺便说一句,您的示例和代码不匹配。您的示例将其设置为使每列都是索引,但它是每行。对于计时测试,我将转置您的结果。我正在Mac OS X 10.10.3上运行MATLAB R2013a,使用16 GB RAM和Intel i7 2.3 GHz处理器。因此:

clear all;
close all;

%// Generate dictionary
chars = {'Apple', 'Banana', 'Cherry', 'Dragonfruit'};

rng(123);

%// Generate 50000 random words
v = randi(numel(chars), 50000, 1);
inputArray = chars(v);
[~,~,inputNum] = unique(inputArray);
inputNum = inputNum.'; %// To make compatible in dimensions

%// Timing #1 - sub2ind
tic;
binMatrix(numel(inputArray), max(inputNum)) = 0;
binMatrix(sub2ind(size(binMatrix), 1:numel(inputArray), inputNum)) = 1;
t = toc;

clear binMatrix;

%// Timing #2 - sparse
tic;
binMatrix = sparse(1:numel(inputArray), inputNum, 1, numel(inputArray), max(inputNum));
binMatrix = full(binMatrix);
t2 = toc;

clear binMatrix;

%// Timing #3 - ismember and for
tic;
binMatrix = zeros(numel(inputArray), numel(chars));
for i = 1: size(binMatrix,1)
binMatrix(i,:) = ismember(chars, inputArray(i));
end
t3 = toc;

%// Timing #4 - bsxfun
clear binMatrix;
tic;
binMatrix = bsxfun(@eq,inputNum',unique(inputNum)); %// Changed to make dimensions match
t4 = toc;

clear binMatrix;

%// Timing #5 - raw sub2ind
tic;
binMatrix(numel(inputArray), max(inputNum)) = 0;
binMatrix( (inputNum-1)*size(binMatrix,1) + [1:numel(inputArray)] ) = 1;
t5 = toc;

fprintf('Timing using sub2ind: %f seconds\n', t);
fprintf('Timing using sparse: %f seconds\n', t2);
fprintf('Timing using ismember and loop: %f seconds\n', t3);
fprintf('Timing using bsxfun: %f seconds\n', t4);
fprintf('Timing using raw sub2ind: %f seconds\n', t5);
我们得到:

Timing using sub2ind: 0.004223 seconds
Timing using sparse: 0.004252 seconds
Timing using ismember and loop: 2.771389 seconds
Timing using bsxfun: 0.020739 seconds
Timing using raw sub2ind: 0.000773 seconds
就级别而言:

  • 原始
    sub2ind
  • sub2ind
  • sparse
  • bsxfun
  • OP法

  • 以下是我对这个问题的看法:

    input = ['ABCDAB']';
    binMatrix = bsxfun(@eq,input,unique(input)');
    
    为了进行基准测试,我在Windows 7机器、4Gb RAM、Intel i7-2600 CPU 3.4 GHz上运行了它,借用了@rayryeng初始化代码:

    % Generate dictionary from A up to I
    ch = char(65 + (0:8));
    
    rng(123);
    
    % Generate 50000 random characters
    v = randi(9, 50000, 1);
    inputArray = ch(v);
    
    time=0;
    for ii=1:100
        tic;
        binMatrix = bsxfun(@eq,inputArray,unique(inputArray)');
        t = toc;
        time=time+t;
    end
    disp(time/100);
    

    这给了我0.001203秒。有关方法的广泛比较,请参考@ryaryeng的答案。

    如果您不介意在输入数组中有非连续字符的情况下使用全零列,例如
    'ABEACF'
    ,其中
    'D'
    缺失,您可以使用此选项-

    col_idx = inputArray - 'A' + 1;
    binMatrix(numel(inputArray), max(col_idx) ) = 0;
    binMatrix( (col_idx-1)*size(binMatrix,1) + [1:numel(inputArray)] ) = 1;
    
    如果您确实关心这个问题,并且不希望出现全零列,那么可以使用它的修改版本-

    [~,unq_pos,col_idx] = unique(inputArray,'stable');
    binMatrix(numel(inputArray), numel(unq_pos)) = 0;
    binMatrix( (col_idx-1)*size(binMatrix,1) + [1:numel(inputArray)].' ) = 1;
    

    基本上,这两种方法都使用相同的黑客技术进行预分配,如中所列,也如中所列。除此之外,它还使用了原始版本的
    sub2ind

    @Divakar-哦,我知道。我一直在使用预分配黑客。我甚至不再使用零。如果您好奇的话,我添加了计时测试,还添加了使用
    sparse
    方法。看起来
    sparse
    稍微快一点。哈!有趣,可能是未来有用的知识@rayryeng感谢您的测试,特别是这次黑客攻击。必须付诸行动!使用了更多的黑客行为来摆脱
    sub2ind
    ,并继续使用预分配黑客!希望,没关系!:)@rayryeng对于
    v
    ,数据量更大,为
    20000000
    ,我得到了这些值-。我跳过了ismember,因为计算时间太长了。我猜你的答案读得太多了。有一天我必须学会它;)做了一些运行时测试,似乎这是相当有效的,我可以想到,在使用它做得很好!!酷!你有没有试着像OP一样在50000长度的向量上使用它?这只在六元素向量上完成。@Divakar-做了定时测试<代码>bsxfun速度较慢。我会更新我的帖子。原始
    sub2ind
    wins!捕捉得好@rayryeng计时仅针对六元素阵列完成。我会尽快编辑,以免误导!我们帮了什么忙?考虑一下接受一个我们的答案。我已经编辑了我的帖子来回复你的编辑。它只需要一个非常小的编辑,但我们的方法仍然比你的快。要使用我们的任何方法,您只需调用
    unique
    ,然后将第三个输出用于我们的任何算法。
    [~,unq_pos,col_idx] = unique(inputArray,'stable');
    binMatrix(numel(inputArray), numel(unq_pos)) = 0;
    binMatrix( (col_idx-1)*size(binMatrix,1) + [1:numel(inputArray)].' ) = 1;