MatrixBerryCore
ismemberrows.m
Go to the documentation of this file.
1 function [isMemberVec,indMemberVec]=ismemberrows(inpLeftMat,inpRightMat,isInteger,forceMode)
2 import mxberry.core.throwerror;
3 persistent maxVal sqMaxVal logMaxVal;
4 %
5 if nargin<3
6  isInteger=false;
7 end
8 [nLeftRows,nCols]=size(inpLeftMat);
9 if size(inpRightMat,2)~=nCols
10  throwerror('wrongInput',...
11  'Number of columns in inpLeftMat and inpRightMat must be the same');
12 end
13 nRightRows=size(inpRightMat,1);
14 isInd=nargout>1;
15 if nLeftRows==0||nRightRows==0||nCols==0
16  if nCols==0
17  isMemberVec=true(nLeftRows,1);
18  indMemberVec=repmat(nRightRows,nLeftRows,1);
19  else
20  isMemberVec=false(nLeftRows,1);
21  indMemberVec=zeros(nLeftRows,1);
22  end
23  return;
24 end
25 if ~isInteger
26  isNum=isnumeric(inpLeftMat)&&isnumeric(inpRightMat);
27  if isNum
28  if ~(isreal(inpLeftMat)&&isreal(inpRightMat))
29  % transform complex numbers to real ones separating them on real
30  % and imaginery parts
31  nCols=2*nCols;
32  inpLeftMat=[real(inpLeftMat) imag(inpLeftMat)];
33  inpRightMat=[real(inpRightMat) imag(inpRightMat)];
34  end
35  end
36 end
37 if nCols<2
38  % for simple situation use ismember
39  if ~isInteger
40  % find nans
41  isLeftMat=isnan(inpLeftMat);
42  isRightMat=isnan(inpRightMat);
43  isLeftNan=any(isLeftMat);
44  isRightNan=any(isRightMat);
45  isInteger=~(isLeftNan||isRightNan);
46  end
47  if isInteger
48  % perform ismember
49  if isInd
50  [isMemberVec,indMemberVec]=ismember(inpLeftMat,inpRightMat,'legacy');
51  else
52  isMemberVec=ismember(inpLeftMat,inpRightMat,'legacy');
53  end
54  else
55  isMemberVec=false(nLeftRows,1);
56  indMemberVec=zeros(nLeftRows,1);
57  % perform ismember for NaNs
58  if isLeftNan
59  if isRightNan
60  isMemberVec(isLeftMat)=true;
61  indMemberVec(isLeftMat)=find(isRightMat,1,'last');
62  end
63  end
64  if isLeftNan||isRightNan
65  isLeftMat=~isLeftMat;
66  isRightMat=~isRightMat;
67  end
68  % peform ismember for non-NaNs
69  if isInd
70  [isCurMemberVec,indCurMemberVec]=ismember(inpLeftMat(isLeftMat),inpRightMat(isRightMat),'legacy');
71  if isRightNan&&any(isCurMemberVec)
72  inpRightMat=find(isRightMat);
73  indCurMemberVec(isCurMemberVec)=inpRightMat(indCurMemberVec(isCurMemberVec));
74  end
75  isMemberVec(isLeftMat)=isCurMemberVec;
76  indMemberVec(isLeftMat)=indCurMemberVec;
77  else
78  isMemberVec(isLeftMat)=ismember(inpLeftMat(isLeftMat),inpRightMat(isRightMat),'legacy');
79  end
80  end
81 else
82  % initial actions
83  isForceMode=nargin>=4;
84  if ~strcmp(class(inpLeftMat),class(inpRightMat))
85  throwerror('wrongInput',...
86  'Classes of inpLeftMat and inpRightMat differ');
87  end
88  if isempty(maxVal)||isempty(sqMaxVal)||isempty(logMaxVal)
89  maxVal=1/eps('double');
90  sqMaxVal=sqrt(maxVal);
91  logMaxVal=log2(maxVal);
92  end
93  isnOptimized=true;
94  isIntType=isinteger(inpLeftMat);
95  isLogicalType=islogical(inpLeftMat);
96  isCharType=ischar(inpLeftMat);
97  isInteger=isInteger||isIntType||isLogicalType||isCharType;
98  isReshape=~isInteger;
99  if isReshape
100  if ~isNum
101  throwerror(':wrongInput',...
102  'Type of inpLeftMat and inpRightMat is wrong');
103  end
104  % reshape matrices into column vectors
105  inpLeftMat=inpLeftMat(:);
106  inpRightMat=inpRightMat(:);
107  isLeftMat=isfinite(inpLeftMat);
108  isRightMat=isfinite(inpRightMat);
109  if (all(isLeftMat)&&all(isRightMat))
110  minInpVal=min(min(inpLeftMat),min(inpRightMat));
111  maxInpVal=max(max(inpLeftMat),max(inpRightMat));
112  else
113  % replace non-finite numbers by finite ones
114  inpMat=[inpLeftMat(isLeftMat);inpRightMat(isRightMat)];
115  % determine range of finite values
116  if isempty(inpMat)
117  minInpVal=0;
118  maxInpVal=0;
119  else
120  minInpVal=min(inpMat);
121  maxInpVal=max(inpMat);
122  end
123  isLeftMat=~isLeftMat;
124  isRightMat=~isRightMat;
125  inpMat=[inpLeftMat(isLeftMat);inpRightMat(isRightMat)];
126  % replace -Inf
127  isMat=inpMat==-Inf;
128  if any(isMat)
129  curVal=minInpVal;
130  nextVal=curVal-1;
131  if nextVal==curVal
132  nextVal=2*curVal;
133  end
134  minInpVal=nextVal;
135  inpMat(isMat)=nextVal;
136  end
137  % replace Inf
138  isMat=inpMat==Inf;
139  if any(isMat)
140  curVal=maxInpVal;
141  nextVal=curVal+1;
142  if nextVal==curVal
143  nextVal=2*curVal;
144  end
145  maxInpVal=nextVal;
146  inpMat(isMat)=nextVal;
147  end
148  % replace NaN
149  isMat=isnan(inpMat);
150  if any(isMat)
151  curVal=maxInpVal;
152  if curVal==Inf
153  throwerror('wrongInput',...
154  ['Range of values in inpLeftMat and inpRightMat is ',...
155  'too large to process it correctly']);
156  end
157  nextVal=curVal+1;
158  if nextVal==curVal
159  nextVal=2*curVal;
160  end
161  maxInpVal=nextVal;
162  inpMat(isMat)=nextVal;
163  end
164  % update non-finite values
165  curVal=sum(isLeftMat);
166  inpLeftMat(isLeftMat)=inpMat(1:curVal);
167  inpRightMat(isRightMat)=inpMat(curVal+1:end);
168  end
169  rangeVal=maxInpVal-minInpVal+1;
170  if rangeVal<=sqMaxVal
171  isInteger=all(fix(inpLeftMat)==inpLeftMat)&&...
172  all(fix(inpRightMat)==inpRightMat);
173  isnOptimized=~isInteger;
174  end
175  end
176  if isInteger&&isnOptimized
177  % calculate range of values
178  if isLogicalType
179  minInpVal=0;
180  maxInpVal=1;
181  elseif isCharType
182  minInpVal=0;
183  maxInpVal=double(intmax('uint16'));
184  else
185  if ~isReshape
186  isReshape=true;
187  inpLeftMat=inpLeftMat(:);
188  inpRightMat=inpRightMat(:);
189  end
190  minInpVal=double(min(min(inpLeftMat),min(inpRightMat)));
191  maxInpVal=double(max(max(inpLeftMat),max(inpRightMat)));
192  end
193  % determine whether optimized version may be performed or not
194  rangeVal=maxInpVal-minInpVal+1;
195  isnOptimized=rangeVal>sqMaxVal;
196  end
197  % reshape inpLeftMat and inpRightMat from column vectors into matrices if
198  % necessary
199  if isReshape
200  inpLeftMat=reshape(inpLeftMat,nLeftRows,nCols);
201  inpRightMat=reshape(inpRightMat,nRightRows,nCols);
202  end
203  if isForceMode
204  isnOptimized=isnOptimized||~strcmpi(forceMode,'optimized');
205  elseif ~isnOptimized
206  % determine what version (standard or optimized) is to be used
207  nRows=nLeftRows+nRightRows;
208  if rangeVal<=pow2(logMaxVal/nCols)
209  isOptimized=nRows>=3;
210  elseif nCols>=250&&nRows>=100
211  isOptimized=rangeVal<=pow2(nRows^3.62,-24);
212  else
213  isOptimized=rangeVal<=pow2(nRows^3.62,-34);
214  end
215  isnOptimized=~isOptimized;
216  end
217  if isnOptimized
218  % perform built-in version of ismember
219  if isInd
220  [isMemberVec,indMemberVec]=ismember(inpLeftMat,inpRightMat,...
221  'rows');
222  else
223  isMemberVec=ismember(inpLeftMat,inpRightMat,'rows');
224  end
225  return;
226  end
227  % unite all values in single matrix
228  inpLeftMat=[inpLeftMat;inpRightMat];
229  clear inpRightMat;
230  inpLeftMat=double(inpLeftMat)+(1-minInpVal);
231  % calculate codes for rows
232  allSizeVec=max(inpLeftMat,[],1);
233  while nCols>1
234  iCol=0;
235  lenVec=[];
236  % break all columns on segments
237  while iCol<nCols
238  curInd=max(find(cumprod(allSizeVec(iCol+1:end))<=maxVal,1,...
239  'last'),2);
240  if isempty(curInd)
241  curInd=2;
242  end
243  lenVec=horzcat(lenVec,curInd); %#ok<AGROW>
244  iCol=iCol+curInd;
245  end
246  % perform num2cell(inpLeftMat,1)
247  auxCell=cell(1,nCols);
248  for iCol=1:nCols
249  auxCell{iCol}=inpLeftMat(:,iCol);
250  end
251  nCurCols=nCols;
252  nCols=numel(lenVec);
253  if nCols==1
254  % get column vector with codes
255  inpLeftMat=sub2ind(allSizeVec,auxCell{:});
256  else
257  inpLeftMat=inpLeftMat(:,1:nCols);
258  sizeVec=nan(1,nCols);
259  % adjust lenVec
260  lenVec(end)=lenVec(end)+nCurCols-sum(lenVec);
261  % if necessary, process last segment with single column
262  if lenVec(end)==1
263  inpLeftMat(:,nCols)=auxCell{nCurCols};
264  sizeVec(nCols)=allSizeVec(nCurCols);
265  nCurCols=nCols-1;
266  else
267  nCurCols=nCols;
268  end
269  % get codes for all segments
270  leftIndVec=[1 cumsum(lenVec(1:nCurCols-1))+1];
271  for iCol=1:nCurCols
272  curInd=leftIndVec(iCol)+(0:lenVec(iCol)-1);
273  [uniqueLinInd,~,inpLeftMat(:,iCol)]=unique(...
274  sub2ind(allSizeVec(curInd),auxCell{curInd}),'legacy');
275  sizeVec(iCol)=length(uniqueLinInd);
276  end
277  allSizeVec=sizeVec;
278  end
279  end
280  % perform built-in ismember for codes
281  if nCols==0
282  isMemberVec=true(nLeftRows,1);
283  indMemberVec=nRightRows*ones(nLeftRows,1);
284  else
285  if isInd
286  [isMemberVec,indMemberVec]=ismember(inpLeftMat(1:nLeftRows),...
287  inpLeftMat(nLeftRows+1:end),'legacy');
288  else
289  isMemberVec=ismember(inpLeftMat(1:nLeftRows),...
290  inpLeftMat(nLeftRows+1:end),'legacy');
291  end
292  end
293 end
function num2cell(in inpArray, in varargin)
NUM2CELL is an extension of Matlab built-in function "num2cell" designed to work correctly with empty...
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 ismemberrows(in inpLeftMat, in inpRightMat, in isInteger, in forceMode)
ISMEMBERROWS finds indices of rows from the first matrix in the second matrix, i.e. it is the more efficient version of ISMEMBER(. . .,&#39;rows&#39;)
function repmat(in inpArray, in varargin)
function unique(in inpVec)
UNIQUE for arrays of any type.
function ismember(in leftVec, in rightVec, in varargin)
ISMEMBER - ismember implementation for arrays of any type.