@@ -41,47 +41,60 @@ Base.length(x::RowIndexMap) = length(x.orig)
41
41
42
42
# composes the joined data table using the maps between the left and right
43
43
# table rows and the indices of rows in the result
44
- function compose_joined_table (joiner:: DataTableJoiner , kind :: Symbol ,
44
+ function compose_joined_table (joiner:: DataTableJoiner ,
45
45
left_ixs:: RowIndexMap , leftonly_ixs:: RowIndexMap ,
46
46
right_ixs:: RowIndexMap , rightonly_ixs:: RowIndexMap )
47
47
@assert length (left_ixs) == length (right_ixs)
48
48
# compose left half of the result taking all left columns
49
49
all_orig_left_ixs = vcat (left_ixs. orig, leftonly_ixs. orig)
50
- if length (leftonly_ixs) > 0
50
+
51
+ lil = length (left_ixs)
52
+ loil = length (leftonly_ixs)
53
+ ril = length (right_ixs)
54
+ roil = length (rightonly_ixs)
55
+
56
+ if loil > 0
51
57
# combine the matched (left_ixs.orig) and non-matched (leftonly_ixs.orig) indices of the left table rows
52
58
# preserving the original rows order
53
- all_orig_left_ixs = similar (left_ixs. orig, length (left_ixs) + length (leftonly_ixs) )
59
+ all_orig_left_ixs = similar (left_ixs. orig, lil + loil )
54
60
@inbounds all_orig_left_ixs[left_ixs. join] = left_ixs. orig
55
61
@inbounds all_orig_left_ixs[leftonly_ixs. join] = leftonly_ixs. orig
56
62
else
57
63
# the result contains only the left rows that are matched to right rows (left_ixs)
58
64
all_orig_left_ixs = left_ixs. orig # no need to copy left_ixs.orig as it's not used elsewhere
59
65
end
60
- ril = length (right_ixs)
61
- loil = length (leftonly_ixs)
62
- roil = length (rightonly_ixs)
63
- left_dt = DataTable (Any[resize! (col[all_orig_left_ixs], length (all_orig_left_ixs)+ roil)
64
- for col in columns (joiner. dtl)],
65
- names (joiner. dtl))
66
66
67
- # compose right half of the result taking all right columns excluding on
68
- dtr_noon = without (joiner. dtr, joiner. on_cols)
69
67
# permutation to swap rightonly and leftonly rows
70
68
right_perm = vcat (1 : ril, ril+ roil+ 1 : ril+ roil+ loil, ril+ 1 : ril+ roil)
71
69
if length (leftonly_ixs) > 0
72
70
# compose right_perm with the permutation that restores left rows order
73
71
right_perm[vcat (right_ixs. join, leftonly_ixs. join)] = right_perm[1 : ril+ loil]
74
72
end
75
73
all_orig_right_ixs = vcat (right_ixs. orig, rightonly_ixs. orig)
76
- resizelen = length (all_orig_right_ixs)+ length (leftonly_ixs)
77
- rightcols = Any[kind == :inner ?
78
- col[all_orig_right_ixs][right_perm] :
79
- copy! (similar_nullable (col, resizelen), col[all_orig_right_ixs])[right_perm]
80
- for col in columns (dtr_noon)]
81
- right_dt = DataTable (rightcols, names (dtr_noon))
82
- # merge left and right parts of the joined table
83
- res = hcat! (left_dt, right_dt)
84
74
75
+ # compose right half of the result taking all right columns excluding on
76
+ dtr_noon = without (joiner. dtr, joiner. on_cols)
77
+
78
+ laoli = length (all_orig_left_ixs)
79
+ laori = length (all_orig_right_ixs)
80
+ @assert laoli + roil == laori + loil
81
+ numrows = length (all_orig_left_ixs) + roil
82
+ numcols = ncol (joiner. dtl) + ncol (dtr_noon)
83
+
84
+ # if either size is smaller, then it's null
85
+ leftnull = laoli < laoli + roil
86
+ rightnull = laori < laori + loil
87
+ dtcols = Vector {Any} (numcols)
88
+ for (i,col) in enumerate (columns (joiner. dtl))
89
+ dtcols[i] = leftnull ? copy! (similar_nullable (col, numrows), col[all_orig_left_ixs]) :
90
+ col[all_orig_left_ixs]
91
+ end
92
+ for (i,col) in enumerate (columns (dtr_noon))
93
+ dtcols[i+ ncol (joiner. dtl)] = rightnull ? copy! (similar_nullable (col, numrows), col[all_orig_right_ixs])[right_perm] :
94
+ col[all_orig_right_ixs][right_perm]
95
+ end
96
+ colnames = vcat (names (joiner. dtl), names (dtr_noon))
97
+ res = DataTable (dtcols, Index (colnames))
85
98
if length (rightonly_ixs. join) > 0
86
99
# some left rows are nulls, so the values of the "on" columns
87
100
# need to be taken from the right
@@ -207,6 +220,8 @@ join(dt1::AbstractDataTable,
207
220
- `:cross` : a full Cartesian product of the key combinations; every
208
221
row of `dt1` is matched with every row of `dt2`
209
222
223
+ For the three join operations that may introduce missing values, `:outer`, `:left`,
224
+ and `:right`,
210
225
Null values are filled in where needed to complete joins.
211
226
212
227
### Result
@@ -243,22 +258,21 @@ function Base.join(dt1::AbstractDataTable,
243
258
joiner = DataTableJoiner (dt1, dt2, on)
244
259
245
260
if kind == :inner
246
- compose_joined_table (joiner, kind, update_row_maps! (joiner. dtl_on, joiner. dtr_on,
247
- group_rows (joiner. dtr_on),
248
- true , false , true , false )... )
261
+ compose_joined_table (joiner, update_row_maps! (joiner. dtl_on, joiner. dtr_on,
262
+ group_rows (joiner. dtr_on),
263
+ true , false , true , false )... )
249
264
elseif kind == :left
250
- compose_joined_table (joiner, kind, update_row_maps! (joiner. dtl_on, joiner. dtr_on,
251
- group_rows (joiner. dtr_on),
252
- true , true , true , false )... )
265
+ compose_joined_table (joiner, update_row_maps! (joiner. dtl_on, joiner. dtr_on,
266
+ group_rows (joiner. dtr_on),
267
+ true , true , true , false )... )
253
268
elseif kind == :right
254
- right_ixs, rightonly_ixs, left_ixs, leftonly_ixs = update_row_maps! (joiner. dtr_on, joiner. dtl_on,
255
- group_rows (joiner. dtl_on),
256
- true , true , true , false )
257
- compose_joined_table (joiner, kind, left_ixs, leftonly_ixs, right_ixs, rightonly_ixs)
269
+ compose_joined_table (joiner, update_row_maps! (joiner. dtr_on, joiner. dtl_on,
270
+ group_rows (joiner. dtl_on),
271
+ true , true , true , false )[[3 , 4 , 1 , 2 ]]. .. )
258
272
elseif kind == :outer
259
- compose_joined_table (joiner, kind, update_row_maps! (joiner. dtl_on, joiner. dtr_on,
260
- group_rows (joiner. dtr_on),
261
- true , true , true , true )... )
273
+ compose_joined_table (joiner, update_row_maps! (joiner. dtl_on, joiner. dtr_on,
274
+ group_rows (joiner. dtr_on),
275
+ true , true , true , true )... )
262
276
elseif kind == :semi
263
277
# hash the right rows
264
278
dtr_on_grp = group_rows (joiner. dtr_on)
0 commit comments