如果不是所有变量都相同,如何连接两个数据集?

时间:2013-07-12 15:41:24

标签: matlab

我有两个列数不同的数据集:

DS1:

A   B   C
1   2   3

DS2:

A   C   D
2   3   4

现在我想合并这两个数据集:

结果:

A   B   C   D
1   2   3   0
2   3   0   4

正如您所看到的,如果两个数据集中都没有变量名,我只想添加0,NaN或空白。我尝试使用catjoin,但我无法弄清楚如何做到这一点。任何提示?

3 个答案:

答案 0 :(得分:3)

这是一种丑陋的方式 - 然后是一个更清洁的方式(稍后添加)。问题是,只要您使用单元格数组(因为数据类型是混合的 - 列的字母,然后是数字)生活变得困难。你可以通过创建一个结构来做得更好,其中列名和数据是两个独立的数组(见下文)......但是现在这里是“一个解决方案”。通过在两个数据集中使用不同数量的行以及不同数量的列,我使生活变得更有趣 - 只是为了确保不会破坏某些内容。

ds1 = {'a','bb','c';1,2,3};
ds2 = {'aa','c','d', 'e';2,3,4,5; 5,6,7,8};
cols = unique({ds1{1,:} ds2{1,:}});

ds3 = cols;
n1 = size(ds1,1) - 1;
%%
for ii = 1:size(ds1,2)
    ci = find(cellfun(@(x) isequal(x, ds1{1,ii}), cols));
    if numel(ci) > 0
        for jj = 1:n1
            ds3{1+jj,ci} = ds1{1+jj, ii};
        end
    end
end
n2 = size(ds2, 1) - 1;
for ii = 1:size(ds2,2)
    ci = find(cellfun(@(x) isequal(x, ds2{1,ii}), cols));
    if numel(ci) > 0
        for jj = 1:n2
            ds3{1+n1+jj,ci} = ds2{1+jj, ii};
        end
    end
end

生成的合并数组:

'a'   'aa'  'bb'   'c'    'd'   'e'
[1]     []   [ 2]   [3]    []    []
 []   [ 2]     []   [3]   [4]   [5]
 []   [ 5]     []   [6]   [7]   [8]

不是最佳的,我敢肯定 - 但它确实按照你的要求行事......我讨厌在循环中做这件事,但却找不到解决方法。我希望其中一位“真正的Matlab专家”会在他看到这一点时呕吐,并激励你给出聪明的一行答案。

编辑我更多地考虑了这一点,并提出了一种更有效的算法:

% assuming column headers and data are in two separate arrays
ds1headers = {'a','bb','c'};
ds1data = [1 2 3; 2 3 4];
ds2headers = {'aa','c','d', 'e'};
ds2data = [2 3 4 5; 3 4 5 6; 4 5 6 7];

% as before, find unique column headers:
cols = unique({ds1headers{:} ds2headers{:}});

% convert to column numbers:
ds1conv = cellfun(@(x)find(ismember(cols, x)), ds1headers);
ds2conv = cellfun(@(x)find(ismember(cols, x)), ds2headers);

% now conversion is easy:
n1 = size(ds1data,1);
n2 = size(ds2data,1);
ds3data = zeros(n1+n2, numel(cols));

ds3data(1:n1, ds1conv) = ds1data;
ds3data(n1+(1:n2), ds2conv) = ds2data;

disp(cols)
disp(ds3data)

结果是

'a'    'aa'    'bb'    'c'    'd'    'e'

 1     0     2     3     0     0
 2     0     3     4     0     0
 0     2     0     3     4     5
 0     3     0     4     5     6
 0     4     0     5     6     7

看起来它会起作用 - 并且没有丑陋的循环......我现在认识到这看起来有点像下面的@Magla解决方案(当我发布我的更新时没有看到它,但它显然在那之前我的最新编辑) - 除了我仍然有列名称的单元格数组,以及其他一些改进。

答案 1 :(得分:3)

我会选择这样的东西。它用零填充最终矩阵。

%examples (ABCD are replaced by indexes 1234)
A = [1 2 3; 11 12 13];  
B = [1 3 5 8; 111 112 113 114];  

%first mix the first rows of A and B
header = union(A(1,:), B(1,:))  

%find the corresponding indexes in A and B
[Lia,LocbA] = ismember(A(1,:),header);  
[Lia,LocbB] = ismember(B(1,:),header);  


%concatenate the second rows of A and B
C = header  
C(2,LocbA) = A(2,:);  
C(3,LocbB) = B(2,:);  

结果:

A =

 1     2     3
11    12    13

B =

 1     3     5     8
111   112   113   114

C =

 1     2     3     5     8
 11    12    13    0     0
 111   0     112   113   114

编辑:最初提供的代码也适用于单元格(请参阅下面的示例)。在这种情况下,它用空单元填充最终的单元阵列。与@Floris解决方案相反,要合并的数据集由列标题(第一行)和数据(第二行)组成。我想你的数据格式将适合这两种解决方案中的一种。

%input modification (now with cells)
A = {'A' 'B' 'C';     11  12  13};  
B = {'A' 'C' 'E' 'H'; 111 112 113 114};  

结果:

C =

'A'      'B'     'C'      'E'      'H'  
[ 11]    [12]    [ 13]    []       []
[111]    []      [112]    [113]    [114]

答案 2 :(得分:1)

首先我们创建示例:

% Create test datasets:
A=1; 
B=2;
C=3;
save db1
A=2;
clear B;
D=4;
save db2
clear;

现在脚本看起来或多或少像:

% Your script starts here, replace your paths with the correct paths:
path_to_db1 = 'db1';
path_to_db2 = 'db2';

db1 = load(path_to_db1);
db2 = load(path_to_db2);

merge = db1;

for field = fieldnames(db1)'
  field = field{1};
  if isfield(db2,field)
    merge.(field) = [merge.(field);db2.(field)];
  else
    merge.(field) = [merge.(field);0];
  end 
end

for field = fieldnames(db2)'
  field = field{1};
  if ~isfield(db1,field)
    merge.(field) = [0;db2.(field)];
  end
end
clear db1 db2;

输出:

>> merge.A

ans =

     1
     2

>> merge.B 

ans =

     2
     0

>> merge.C

ans =

     3
     3

>> merge.D 

ans =

     0
     4

但您可能希望它们是工作空间中的自由变量,而不是合并结构,因此您可以添加以下代码:

for field = fieldnames(merge)'
  field=field{1};
  eval(sprintf('%s = merge.%s;',field,field));
end