MatrixBerryCore
uniquejoint.m
Go to the documentation of this file.
1 function [unqCArr,indRight2LeftVec,indLeft2RightVec,isSorted]=uniquejoint(inpCArr,varargin)
2 import mxberry.core.throwerror;
3 import mxberry.core.uniquejoint;
4 import mxberry.core.uniquebyfunc;
5 import mxberry.core.uniquesortableobj;
6 %
7 if nargin<1
8  throwerror('wrongInput',...
9  'incorrect number of input arguments');
10 end
11 %
12 if ~iscell(inpCArr)
13  throwerror('wrongInput',...
14  'cell array expected as the first argument');
15 end
16 %
17 nInp=numel(inpCArr);
18 %
19 if nInp==0
20  throwerror('wrongInput',...
21  'an input argument should be non-empty cell array');
22 end
23 %
24 [reg,prop]=parseparams(varargin);
25 nReg=numel(reg);
26 if nReg<1
27  indDim=[];
28 else
29  indDim=reg{1};
30  isnWrong=isnumeric(indDim)&&numel(indDim)==1;
31  if isnWrong
32  indDim=double(indDim);
33  isnWrong=isreal(indDim)&&floor(indDim)==indDim&&indDim>=1&&...
34  isfinite(indDim);
35  end
36  if ~isnWrong
37  throwerror('wrongInput',...
38  'scalar number expected as the second argument');
39  end
40 end
41 %
42 if isempty(indDim)
43  inpSizeVec=size(inpCArr{1});
44  isEqualSize=mxberry.core.checksize(inpCArr{:},inpSizeVec);
45  if ~isEqualSize
46  throwerror('wrongInput',...
47  'size of all items of the first cell array should be the same');
48  end
49  %
50  isnColumn=inpSizeVec(1)<=1;
51  %turn all cell elements into columns
52  if isnColumn
53  inpArr=cellfun(@transpose,inpCArr,'UniformOutput',false);
54  else
55  inpArr=inpCArr;
56  end
57  %
58  nInpElem=numel(inpArr{1});
59  lengthInp=max(inpSizeVec);
60 else
61  nInpElem=size(inpCArr{1},indDim);
62  %
63  %cellfun('size',...,dim) doesn't work properly for cell arrays of
64  %enums so we are forced to use a slower variant here: cellfun(@(x)...)
65  isEqualSize=all(cellfun(@(x)size(x,indDim),inpCArr(:))==nInpElem);
66  %
67  if ~isEqualSize
68  throwerror('wrongInput',...
69  ['size of all items in inpCell along %d-th ',...
70  'dimension should be the same'],indDim);
71  end
72  %
73  inpSizeVec=cellfun(@size,inpCArr,'UniformOutput',false);
74  nDimsVec=cellfun('length',inpSizeVec(:));
75  if indDim>1
76  permVec=[indDim 1:indDim-1 indDim+1:max(nDimsVec)];
77  inpArr=cellfun(@(x)reshape(permute(x,permVec),nInpElem,[]),...
78  inpCArr,'UniformOutput',false);
79  else
80  inpArr=inpCArr;
81  isReshape=cellfun('length',inpSizeVec)>2;
82  if any(isReshape)
83  inpArr(isReshape)=cellfun(@(x)reshape(x,nInpElem,[]),...
84  inpArr(isReshape),'UniformOutput',false);
85  end
86  end
87  lengthInp=nInpElem;
88 end
89 %
90 if nInpElem==0
91  if lengthInp>0
92  unqCArr=cellfun(@(x)x(1,:),inpArr,'UniformOutput',false);
93  if isnColumn
94  unqCArr=cellfun(@transpose,unqCArr,'UniformOutput',false);
95  end
96  indRight2LeftVec=1;
97  indLeft2RightVec=ones(1,lengthInp);
98  else
99  unqCArr=inpCArr;
100  if isempty(indDim)
101  indRight2LeftVec=[];
102  indLeft2RightVec=[];
103  elseif indDim==1
104  indRight2LeftVec=nan(0,1);
105  indLeft2RightVec=nan(0,1);
106  else
107  indRight2LeftVec=nan(1,0);
108  indLeft2RightVec=nan(1,0);
109  end
110  end
111  return;
112 end
113 %
114 if isempty(indDim)
115  if (nInpElem~=lengthInp)
116  throwerror('wrongInput',...
117  'all cell items should be either columns or rows ');
118  end
119 else
120  inpCArr=inpArr;
121 end
122 %
123 %apply iterative unique operation
124 indMat=zeros(nInpElem,nInp);
125 isnSorted=false(1,nInp);
126 for iRow=1:nInp
127  inpMat=inpArr{iRow};
128  if isa(inpMat,'function_handle')
129  inpMat=func2str(inpMat);
130  end
131  if isnumeric(inpMat)||islogical(inpMat)||ischar(inpMat)
132  [~,~,indMat(:,iRow)]=mxberry.core.uniquerows(inpMat,false,prop{:});
133  elseif isstruct(inpMat)
134  curMat=reshape(mxberry.core.num2cell(...
135  permute(struct2cell(inpMat),[2 3 1]),[1 2]),1,[]);
136  [~,~,indMat(:,iRow)]=uniquejoint(curMat,1);
137  else
138  nCols=size(inpMat,2);
139  isCharStr=iscellstr(inpMat);
140  isNumCell=iscell(inpMat)&&~isCharStr;
141  isStructCell=false;
142  isCharCell=false;
143  isIntCell=false;
144  if isNumCell
145  inpMat=inpMat(:);
146  isIntCell=all(cellfun(@(x)isinteger(x)||islogical(x),inpMat));
147  if ~isIntCell
148  isNumCell=all(cellfun(@isnumeric,inpMat));
149  end
150  if ~isNumCell
151  isStructCell=all(cellfun('isclass',inpMat,'struct'));
152  if ~isStructCell
153  isCharStr=all(cellfun('isclass',inpMat,'function_handle'));
154  if isCharStr
155  inpMat=cellfun(@func2str,inpMat,'UniformOutput',false);
156  else
157  isCharCell=all(cellfun(@iscellstr,inpMat));
158  end
159  end
160  end
161  inpMat=reshape(inpMat,[],nCols);
162  end
163  indColMat=nan(nInpElem,nCols);
164  if isCharStr
165  for iCol=1:nCols
166  [~,~,indColMat(:,iCol)]=unique(inpMat(:,iCol),'legacy');
167  end
168  elseif isNumCell||isStructCell||isCharCell
169  for iCol=1:nCols
170  inpVec=inpMat(:,iCol);
171  curIndMat=zeros(nInpElem,2);
172  if isStructCell
173  inpMat=cellfun(@orderfields,inpVec,'UniformOutput',false);
174  curMat=cellfun(@fieldnames,inpVec,'UniformOutput',false);
175  [sizeVars,~,curIndMat(:,1)]=unique(cellfun('prodofsize',curMat),...
176  'legacy');
177  nVars=size(sizeVars,1);
178  for iVar=1:nVars
179  isVar=curIndMat(:,1)==iVar;
180  [~,~,curIndMat(isVar,2)]=uniquejoint({horzcat(curMat{isVar}).'},1);
181  end
182  [~,~,curMat]=mxberry.core.uniquerows(curIndMat,true,prop{:});
183  transformFunc=@(x)reshape(struct2cell(reshape(x,1,numel(x))),1,[]);
184  elseif isCharCell
185  curMat=[];
186  transformFunc=@(x)reshape(x,1,[]);
187  else
188  curMat=[];
189  transformFunc=@(x)reshape(double(x),1,[]);
190  end
191  [sizeVars,~,curIndMat(:,1)]=mxberry.core.uniquerows(...
192  [cellfun('ndims',inpVec),...
193  cellfun('prodofsize',inpVec),curMat],true,prop{:});
194  nVars=size(sizeVars,1);
195  nDims=1;
196  for iVar=1:nVars
197  isVar=curIndMat(:,1)==iVar;
198  curMat=[cellfun(@size,inpVec(isVar),'UniformOutput',false),...
199  cellfun(transformFunc,inpVec(isVar),'UniformOutput',false)];
200  nElems=size(curMat,1);
201  curMat=horzcat(curMat{:});
202  if isNumCell
203  nDims=sizeVars(iVar,1);
204  end
205  curMat=[...
206  transpose(reshape(curMat(1:nDims*nElems),[],nElems)),...
207  transpose(reshape(curMat(nDims*nElems+1:end),[],nElems))];
208  if isNumCell
209  [~,~,curIndMat(isVar,2)]=mxberry.core.uniquerows(curMat,isIntCell,prop{:});
210  else
211  curMat=[{vertcat(curMat{:,1})},mxberry.core.num2cell(curMat(:,2:end),1)];
212  [~,~,curIndMat(isVar,2)]=uniquejoint(curMat,1);
213  end
214  end
215  if nVars>1
216  [~,~,indColMat(:,iCol)]=mxberry.core.uniquerows(curIndMat,true,prop{:});
217  else
218  indColMat(:,iCol)=curIndMat(:,2);
219  end
220  end
221  elseif isa(inpMat,'opaque')&&ismethod(inpMat,'sort')
222  for iCol=1:nCols
223  [~,~,indColMat(:,iCol)]=uniquesortableobj(inpMat(:,iCol));
224  end
225  else
226  isnSorted(iRow)=true;
227  for iCol=1:nCols
228  [~,~,indColMat(:,iCol)]=uniquebyfunc(inpMat(:,iCol));
229  end
230  end
231  if nCols==1
232  indMat(:,iRow)=indColMat;
233  else
234  [~,~,indMat(:,iRow)]=mxberry.core.uniquerows(indColMat,true,prop{:});
235  end
236  end
237 end
238 isSorted=~any(isnSorted);
239 if ~isSorted
240  indMat=[indMat(:,~isnSorted) indMat(:,isnSorted)];
241 end
242 %
243 isBackwardInd=nargout>2;
244 if nInp==0
245  indRight2LeftVec=1;
246  if isBackwardInd
247  indLeft2RightVec=ones(nInpElem,1);
248  end
249 elseif nInp==1
250  if isBackwardInd
251  [~,indRight2LeftVec,indLeft2RightVec]=unique(indMat);
252  else
253  [~,indRight2LeftVec]=unique(indMat);
254  end
255 else
256  if isBackwardInd
257  [~,indRight2LeftVec,indLeft2RightVec]=mxberry.core.uniquerows(indMat,true,prop{:});
258  else
259  [~,indRight2LeftVec]=mxberry.core.uniquerows(indMat,true,prop{:});
260  end
261 end
262 %
263 if isempty(indDim)
264  %index inpCell, not inpArray since an initial size should be preserved
265  unqCArr=cellfun(@(x)(x(indRight2LeftVec)),inpCArr,'UniformOutput',false);
266 else
267  if all(nDimsVec>=indDim)
268  nOutElem=length(indRight2LeftVec);
269  inpSizeVec=cellfun(@(x)[nOutElem x([1:indDim-1 indDim+1:end])],inpSizeVec,...
270  'UniformOutput',false);
271  end
272  if indDim>1
273  unqCArr=cellfun(...
274  @(x,y)ipermute(reshape(x(indRight2LeftVec,:),y),permVec),...
275  inpCArr,inpSizeVec,'UniformOutput',false);
276  else
277  unqCArr=cellfun(...
278  @(x,y)reshape(x(indRight2LeftVec,:),y),...
279  inpCArr,inpSizeVec,'UniformOutput',false);
280  end
281 end
function uniquesortableobj(in inpVec)
UNIQUE implementation strictly for sortable entities i.e. for those that have 1) full order defined b...
function throwerror(in msgTag, in varargin)
THROWERROR works similarly to built-in ERROR function in case when there is no output arguments but s...
function uniquerows(in inpMat, in isInteger, in forceMode)
UNIQUEROWS finds unique rows in input matrix, i.e. the more effective version of UNIQUE(. . .,&#39;rows&#39;)
function uniquejoint(in inpCArr, in varargin)
UNIQUEJOINT perform joint unique operation for cell arrays.
function unique(in inpVec)
UNIQUE for arrays of any type.
function parseparams(in args, in propNameList, in nRegExpected, in nPropExpected)
PARSEPARAMS behaves exactly as a built-in Matlab function apart from the incorrect behavior of Matlab...
function uniquebyfunc(in inpVec, in fCompare, in algoName)
UNIQUEBYFUNC unique for arrays of any type where an element comparison is performed by a specified fu...