red_amber 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +56 -22
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +178 -0
  5. data/Gemfile +1 -1
  6. data/LICENSE +1 -1
  7. data/README.md +29 -30
  8. data/benchmark/basic.yml +7 -7
  9. data/benchmark/combine.yml +3 -3
  10. data/benchmark/dataframe.yml +15 -9
  11. data/benchmark/group.yml +6 -6
  12. data/benchmark/reshape.yml +6 -6
  13. data/benchmark/vector.yml +6 -3
  14. data/doc/DataFrame.md +32 -12
  15. data/doc/DataFrame_Comparison.md +65 -0
  16. data/doc/SubFrames.md +11 -0
  17. data/doc/Vector.md +207 -1
  18. data/doc/yard-templates/default/fulldoc/html/css/common.css +6 -0
  19. data/lib/red_amber/data_frame.rb +454 -85
  20. data/lib/red_amber/data_frame_combinable.rb +609 -115
  21. data/lib/red_amber/data_frame_displayable.rb +313 -34
  22. data/lib/red_amber/data_frame_indexable.rb +122 -19
  23. data/lib/red_amber/data_frame_loadsave.rb +78 -10
  24. data/lib/red_amber/data_frame_reshaping.rb +184 -14
  25. data/lib/red_amber/data_frame_selectable.rb +623 -70
  26. data/lib/red_amber/data_frame_variable_operation.rb +452 -35
  27. data/lib/red_amber/group.rb +186 -22
  28. data/lib/red_amber/helper.rb +74 -14
  29. data/lib/red_amber/refinements.rb +26 -6
  30. data/lib/red_amber/subframes.rb +1101 -0
  31. data/lib/red_amber/vector.rb +362 -11
  32. data/lib/red_amber/vector_aggregation.rb +312 -0
  33. data/lib/red_amber/vector_binary_element_wise.rb +506 -0
  34. data/lib/red_amber/vector_selectable.rb +265 -23
  35. data/lib/red_amber/vector_unary_element_wise.rb +529 -0
  36. data/lib/red_amber/vector_updatable.rb +278 -34
  37. data/lib/red_amber/version.rb +2 -1
  38. data/lib/red_amber.rb +13 -1
  39. data/red_amber.gemspec +2 -2
  40. metadata +13 -8
  41. data/doc/image/dataframe/reshaping_DataFrames.png +0 -0
  42. data/lib/red_amber/vector_functions.rb +0 -242
@@ -1,39 +1,118 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RedAmber
4
- # mix-ins for the class DataFrame
4
+ # Mix-in for the class DataFrame
5
5
  module DataFrameVariableOperation
6
6
  # Array is refined
7
7
  using RefineArray
8
8
 
9
- # Pick up variables (columns) to create a new DataFrame
9
+ # Select variables (columns) to create a new DataFrame.
10
10
  #
11
- # @note DataFrame#pick creates a DataFrame with single key.
12
- # DataFrame#[] creates a Vector if single key is specified.
11
+ # @note if a single key is specified, DataFrame#pick generates a DataFrame.
12
+ # On the other hand, DataFrame#[] generates a Vector.
13
13
  #
14
14
  # @overload pick(keys)
15
- # Pick variables by Symbols or Strings.
15
+ # Pick up variables by Symbol(s) or String(s).
16
16
  #
17
17
  # @param keys [Symbol, String, <Symbol, String>]
18
18
  # key name(s) of variables to pick.
19
19
  # @return [DataFrame]
20
- # Picked DataFrame.
20
+ # picked DataFrame.
21
+ # @example Pick up by a key
22
+ # languages
23
+ #
24
+ # # =>
25
+ # #<RedAmber::DataFrame : 4 x 3 Vectors, 0x00000000000cfd8c>
26
+ # Language Creator Released
27
+ # <string> <string> <uint16>
28
+ # 0 Ruby Yukihiro Matsumoto 1995
29
+ # 1 Python Guido van Rossum 1991
30
+ # 2 R Ross Ihaka and Robert Gentleman 1993
31
+ # 3 Rust Graydon Hoare 2001
32
+ #
33
+ # languages.pick(:Language)
34
+ #
35
+ # # =>
36
+ # #<RedAmber::DataFrame : 4 x 1 Vector, 0x0000000000113d20>
37
+ # Language
38
+ # <string>
39
+ # 0 Ruby
40
+ # 1 Python
41
+ # 2 R
42
+ # 3 Rust
43
+ #
44
+ # languages[:Language]
45
+ #
46
+ # # =>
47
+ # #<RedAmber::Vector(:string, size=4):0x000000000010359c>
48
+ # ["Ruby", "Python", "R", "Rust"]
21
49
  #
22
50
  # @overload pick(booleans)
23
- # Pick variables by booleans.
51
+ # Pick up variables by booleans.
24
52
  #
25
- # @param booleans [<true, false, nil>]
26
- # boolean array to pick variables at true.
53
+ # @param booleans [<Booleans, nil>, Vector]
54
+ # boolean array or vecctor to pick up variables at true.
27
55
  # @return [DataFrame]
28
- # Picked DataFrame.
56
+ # picked DataFrame.
57
+ # @example Pick up by booleans
58
+ # languages.pick(true, true, false)
59
+ #
60
+ # # =>
61
+ # #<RedAmber::DataFrame : 4 x 2 Vectors, 0x0000000000066a1c>
62
+ # Language Creator
63
+ # <string> <string>
64
+ # 0 Ruby Yukihiro Matsumoto
65
+ # 1 Python Guido van Rossum
66
+ # 2 R Ross Ihaka and Robert Gentleman
67
+ # 3 Rust Graydon Hoare
68
+ #
69
+ # is_string = languages.vectors.map(&:string?) # [true, true, false]
70
+ # languages.pick(is_string)
71
+ # # =>
72
+ # (same as above)
29
73
  #
30
74
  # @overload pick(indices)
31
- # Pick variables by column indices.
75
+ # Pick up variables by column indices.
32
76
  #
33
77
  # @param indices [Integer, Float, Range<Integer>, Vector, Arrow::Array]
34
- # numeric array to pick variables by column index.
78
+ # numeric array to pick up variables by column index.
79
+ # @return [DataFrame]
80
+ # picked DataFrame.
81
+ # @example Pick up by indices
82
+ # languages.pick(0, 2, 1)
83
+ #
84
+ # # =>
85
+ # #<RedAmber::DataFrame : 4 x 3 Vectors, 0x000000000011cfb0>
86
+ # Language Released Creator
87
+ # <string> <uint16> <string>
88
+ # 0 Ruby 1995 Yukihiro Matsumoto
89
+ # 1 Python 1991 Guido van Rossum
90
+ # 2 R 1993 Ross Ihaka and Robert Gentleman
91
+ # 3 Rust 2001 Graydon Hoare
92
+ #
93
+ # @overload pick
94
+ # Pick up variables by the yielded value from the block.
95
+ # @note Arguments and a block cannot be used simultaneously.
96
+ #
97
+ # @yield [self]
98
+ # the block is called within the context of self.
99
+ # (Block is called by instance_eval(&block). )
100
+ # @yieldreturn [keys, booleans, indices]
101
+ # returns keys, booleans or indices just same as arguments.
35
102
  # @return [DataFrame]
36
- # Picked DataFrame.
103
+ # picked DataFrame.
104
+ # @example Pick up by a block.
105
+ # # same as languages.pick { |df| df.languages.vectors.map(&:string?) }
106
+ # languages.pick { languages.vectors.map(&:string?) }
107
+ #
108
+ # # =>
109
+ # #<RedAmber::DataFrame : 4 x 2 Vectors, 0x0000000000154104>
110
+ # Language Creator
111
+ # <string> <string>
112
+ # 0 Ruby Yukihiro Matsumoto
113
+ # 1 Python Guido van Rossum
114
+ # 2 R Ross Ihaka and Robert Gentleman
115
+ # 3 Rust Graydon Hoare
37
116
  #
38
117
  def pick(*args, &block)
39
118
  if block
@@ -47,9 +126,9 @@ module RedAmber
47
126
  case args
48
127
  in [] | [nil]
49
128
  return DataFrame.new
50
- in [*] if args.symbols?
129
+ in [*] if args.symbol?
51
130
  return DataFrame.create(@table.select_columns(*args))
52
- in [*] if args.booleans?
131
+ in [*] if args.boolean?
53
132
  picker = keys.select_by_booleans(args)
54
133
  return DataFrame.create(@table.select_columns(*picker))
55
134
  in [(Vector | Arrow::Array | Arrow::ChunkedArray) => a]
@@ -60,43 +139,114 @@ module RedAmber
60
139
 
61
140
  return DataFrame.new if picker.compact.empty?
62
141
 
63
- if picker.booleans?
142
+ if picker.boolean?
64
143
  picker = keys.select_by_booleans(picker)
65
144
  return DataFrame.create(@table.select_columns(*picker))
66
145
  end
67
146
  picker.compact!
68
147
  raise DataFrameArgumentError, "some keys are duplicated: #{args}" if picker.uniq!
69
148
 
149
+ return self if picker == keys
150
+
70
151
  DataFrame.create(@table.select_columns(*picker))
71
152
  end
72
153
 
73
- # Drop some variables (columns) to create a remainer DataFrame
154
+ # Drop off some variables (columns) to create a remainer DataFrame.
74
155
  #
75
- # @note DataFrame#drop creates a DataFrame even if it is a single column.
156
+ # @note DataFrame#drop creates a DataFrame even if it is a single column
157
+ # (not a Vector).
76
158
  #
77
159
  # @overload drop(keys)
78
- # Drop variables by Symbols or Strings.
160
+ # Drop off variables by Symbol(s) or String(s).
79
161
  #
80
162
  # @param keys [Symbol, String, <Symbol, String>]
81
163
  # key name(s) of variables to drop.
82
164
  # @return [DataFrame]
83
- # Remainer DataFrame.
165
+ # remainer DataFrame.
166
+ # @example Drop off by a key
167
+ # languages
168
+ #
169
+ # # =>
170
+ # #<RedAmber::DataFrame : 4 x 3 Vectors, 0x00000000000cfd8c>
171
+ # Language Creator Released
172
+ # <string> <string> <uint16>
173
+ # 0 Ruby Yukihiro Matsumoto 1995
174
+ # 1 Python Guido van Rossum 1991
175
+ # 2 R Ross Ihaka and Robert Gentleman 1993
176
+ # 3 Rust Graydon Hoare 2001
177
+ #
178
+ # languages.drop(:Language)
179
+ #
180
+ # # =>
181
+ # #<RedAmber::DataFrame : 4 x 2 Vectors, 0x000000000005805c>
182
+ # Creator Released
183
+ # <string> <uint16>
184
+ # 0 Yukihiro Matsumoto 1995
185
+ # 1 Guido van Rossum 1991
186
+ # 2 Ross Ihaka and Robert Gentleman 1993
187
+ # 3 Graydon Hoare 2001
84
188
  #
85
189
  # @overload drop(booleans)
86
- # Drop variables by booleans.
190
+ # Drop off variables by booleans.
87
191
  #
88
- # @param booleans [<true, false, nil>]
89
- # boolean array of variables to drop at true.
192
+ # @param booleans [<Booleans, nil>, Vector]
193
+ # boolean array or vector of variables to drop at true.
90
194
  # @return [DataFrame]
91
- # Remainer DataFrame.
195
+ # remainer DataFrame.
196
+ # @example Drop off by booleans
197
+ # is_numeric = languages.vectors.map(&:numeric?) # [nil, nil, true]
198
+ # languages.drop(is_numeric)
199
+ #
200
+ # # =>
201
+ # #<RedAmber::DataFrame : 4 x 2 Vectors, 0x0000000000066a1c>
202
+ # Language Creator
203
+ # <string> <string>
204
+ # 0 Ruby Yukihiro Matsumoto
205
+ # 1 Python Guido van Rossum
206
+ # 2 R Ross Ihaka and Robert Gentleman
207
+ # 3 Rust Graydon Hoare
92
208
  #
93
209
  # @overload drop(indices)
94
- # Pick variables by column indices.
210
+ # Drop off variables by column indices.
95
211
  #
96
212
  # @param indices [Integer, Float, Range<Integer>, Vector, Arrow::Array]
97
213
  # numeric array of variables to drop by column index.
98
214
  # @return [DataFrame]
99
- # Remainer DataFrame.
215
+ # remainer DataFrame.
216
+ # @example Drop off by indices
217
+ # languages.drop(2)
218
+ #
219
+ # # =>
220
+ # #<RedAmber::DataFrame : 4 x 2 Vectors, 0x0000000000066a1c>
221
+ # Language Creator
222
+ # <string> <string>
223
+ # 0 Ruby Yukihiro Matsumoto
224
+ # 1 Python Guido van Rossum
225
+ # 2 R Ross Ihaka and Robert Gentleman
226
+ # 3 Rust Graydon Hoare
227
+ #
228
+ # @overload drop
229
+ # Drop off variables by the yielded value from the block.
230
+ # @note Arguments and a block cannot be used simultaneously.
231
+ #
232
+ # @yield [self] the block is called within the context of self.
233
+ # (Block is called by instance_eval(&block). )
234
+ # @yieldreturn [keys, booleans, indices]
235
+ # returns keys, booleans or indices just same as arguments.
236
+ # @return [DataFrame]
237
+ # remainer DataFrame.
238
+ # @example Drop off by a block.
239
+ # # same as languages.drop { |df| df.vectors.map(&:numeric?) }
240
+ # languages.drop { vectors.map(&:numeric?) }
241
+ #
242
+ # # =>
243
+ # #<RedAmber::DataFrame : 4 x 2 Vectors, 0x0000000000154104>
244
+ # Language Creator
245
+ # <string> <string>
246
+ # 0 Ruby Yukihiro Matsumoto
247
+ # 1 Python Guido van Rossum
248
+ # 2 R Ross Ihaka and Robert Gentleman
249
+ # 3 Rust Graydon Hoare
100
250
  #
101
251
  def drop(*args, &block)
102
252
  if block
@@ -106,24 +256,26 @@ module RedAmber
106
256
 
107
257
  args = [instance_eval(&block)]
108
258
  end
109
- return self if args.empty? || empty?
259
+ return self if args.compact.empty? || empty?
110
260
 
111
261
  picker =
112
- if args.symbols?
262
+ if args.symbol?
113
263
  keys - args
114
- elsif args.booleans?
264
+ elsif args.boolean?
115
265
  keys.reject_by_booleans(args)
116
- elsif args.integers?
266
+ elsif args.integer?
117
267
  keys.reject_by_indices(args)
118
268
  else
119
269
  dropper = parse_args(args, n_keys)
120
- if dropper.booleans?
270
+ if dropper.compact.empty?
271
+ return self
272
+ elsif dropper.boolean?
121
273
  keys.reject_by_booleans(dropper)
122
- elsif dropper.symbols?
274
+ elsif dropper.symbol?
123
275
  keys - dropper
124
276
  else
125
277
  dropper.compact!
126
- unless dropper.integers?
278
+ unless dropper.integer?
127
279
  raise DataFrameArgumentError, "Invalid argument #{args}"
128
280
  end
129
281
 
@@ -136,7 +288,85 @@ module RedAmber
136
288
  DataFrame.create(@table.select_columns(*picker))
137
289
  end
138
290
 
139
- # rename variables to create a new DataFrame
291
+ # rename keys (variable/column names) to create a updated DataFrame.
292
+ #
293
+ # @overload rename(key_pairs)
294
+ # Rename by key pairs as a Hash.
295
+ #
296
+ # @param key_pairs [Hash{existing_key => new_key}]
297
+ # key pair(s) of existing name and new name.
298
+ # @return [DataFrame]
299
+ # renamed DataFrame.
300
+ # @example Rename by a Hash
301
+ # comecome
302
+ #
303
+ # # =>
304
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x00000000000037b4>
305
+ # name age
306
+ # <string> <uint8>
307
+ # 0 Yasuko 68
308
+ # 1 Rui 49
309
+ # 2 Hinata 28
310
+ #
311
+ # comecome.rename(:age => :age_in_1993)
312
+ #
313
+ # # =>
314
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x00000000000037c8>
315
+ # name age_in_1993
316
+ # <string> <uint8>
317
+ # 0 Yasuko 68
318
+ # 1 Rui 49
319
+ # 2 Hinata 28
320
+ #
321
+ # @overload rename(key_pairs)
322
+ # Rename by key pairs as an Array of Array.
323
+ #
324
+ # @param key_pairs [<Array[existing_key, new_key]>]
325
+ # key pair(s) of existing name and new name.
326
+ # @return [DataFrame]
327
+ # renamed DataFrame.
328
+ # @example Rename by an Array
329
+ # renamer = [[:name, :heroine], [:age, :age_in_1993]]
330
+ # comecome.rename(renamer)
331
+ #
332
+ # # =>
333
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x00000000000037dc>
334
+ # heroine age_in_1993
335
+ # <string> <uint8>
336
+ # 0 Yasuko 68
337
+ # 1 Rui 49
338
+ # 2 Hinata 28
339
+ #
340
+ # @overload rename
341
+ # Rename by key pairs yielding from block.
342
+ #
343
+ # @yield [self] the block is called within the context of self.
344
+ # (Block is called by instance_eval(&block). )
345
+ # @yieldreturn [<[existing_key, new_key]>, Hash]
346
+ # returns an Array or a Hash just same as arguments.
347
+ # @return [DataFrame]
348
+ # renamed DataFrame.
349
+ # @example Rename by block.
350
+ # df
351
+ #
352
+ # # =>
353
+ # #<RedAmber::DataFrame : 2 x 3 Vectors, 0x000000000000c29c>
354
+ # X Y Z
355
+ # <uint8> <uint8> <uint8>
356
+ # 0 1 3 5
357
+ # 1 2 4 6
358
+ #
359
+ # df.rename { keys.zip(keys.map(&:downcase)) }
360
+ # # or
361
+ # df.rename { [keys, keys.map(&:downcase)].transpose }
362
+ #
363
+ # # =>
364
+ # #<RedAmber::DataFrame : 2 x 3 Vectors, 0x000000000000c364>
365
+ # x y z
366
+ # <uint8> <uint8> <uint8>
367
+ # 0 1 3 5
368
+ # 1 2 4 6
369
+ #
140
370
  def rename(*renamer, &block)
141
371
  if block
142
372
  unless renamer.empty?
@@ -162,11 +392,197 @@ module RedAmber
162
392
  rename_by_hash(key_pairs)
163
393
  end
164
394
 
165
- # assign variables to create a new DataFrame
395
+ # Assign new or updated variables (columns) and create an updated DataFrame.
396
+ # - Array-like variables with new keys will append new columns from right.
397
+ # - Array-like variables with exisiting keys will update corresponding vectors.
398
+ # - Symbol key and String key are considered as the same key.
399
+ # - If assigner is empty or nil, returns self.
400
+ #
401
+ # @overload assign(key_value_pairs)
402
+ # accepts pairs of key and values by an Array or a Hash.
403
+ #
404
+ # @param key_value_pairs [Array<key, array_like>, Hash{key => array_like}]
405
+ # `key` must be a Symbol or a String.
406
+ # `array_like` is column data to be assigned.
407
+ # It must be one of `Vector` or `Arrow::Array` or `Array`.
408
+ # @return [DataFrame]
409
+ # assigned DataFrame.
410
+ # @example Assign a new column
411
+ # comecome
412
+ #
413
+ # # =>
414
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x00000000000280dc>
415
+ # name age
416
+ # <string> <uint8>
417
+ # 0 Yasuko 68
418
+ # 1 Rui 49
419
+ # 2 Hinata 28
420
+ #
421
+ # brothers = ['Santa', nil, 'Momotaro']
422
+ # comecome.assign(brother: brothers)
423
+ # # or
424
+ # comecome.assign({ brother: brothers })
425
+ # # or
426
+ # comecome.assign(:brother, brothers)
427
+ # # or
428
+ # comecome.assign([:brother, brothers])
429
+ #
430
+ # # =>
431
+ # #<RedAmber::DataFrame : 3 x 3 Vectors, 0x000000000004077c>
432
+ # name age brother
433
+ # <string> <uint8> <string>
434
+ # 0 Yasuko 68 Santa
435
+ # 1 Rui 49 (nil)
436
+ # 2 Hinata 28 Momotaro
437
+ #
438
+ # @example Assign new data for a existing column
439
+ # comecome.assign(age: comecome[:age] + 29)
440
+ #
441
+ # # =>
442
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x0000000000065860>
443
+ # name age
444
+ # <string> <uint8>
445
+ # 0 Yasuko 97
446
+ # 1 Rui 78
447
+ # 2 Hinata 57
448
+ #
449
+ # @overload assign
450
+ # accepts block yielding pairs of key and values.
451
+ #
452
+ # @yield [self]
453
+ # the block is called within the context of self.
454
+ # (Block is called by instance_eval(&block). )
455
+ # @yieldreturn [Array<key, array_like>, Hash(key => array_like)]
456
+ # `key` must be a Symbol or a String.
457
+ # `array_like` is column data to be assigned.
458
+ # It must be one of `Vector` or `Arrow::Array` or `Array`.
459
+ # @return [DataFrame]
460
+ # assigned DataFrame.
461
+ # @example Assign new data for a existing column by block
462
+ # comecome.assign { { age: age + 29 } }
463
+ # # or
464
+ # comecome.assign { [:age, age + 29] }
465
+ # # or
466
+ # comecome.assign { [[:age, age + 29]] }
467
+ #
468
+ # # =>
469
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x000000000007d640>
470
+ # name age
471
+ # <string> <uint8>
472
+ # 0 Yasuko 97
473
+ # 1 Rui 78
474
+ # 2 Hinata 57
475
+ #
476
+ # @overload assign(keys)
477
+ # accepts keys from argument and pairs of key and values from block.
478
+ #
479
+ # @param keys [Symbol, String] keys of columns to create or update.
480
+ # @yield [self]
481
+ # the block is called within the context of self.
482
+ # (Block is called by instance_eval(&block).)
483
+ # @yieldreturn [Array<array_like>]
484
+ # column data to be assigned.
485
+ # `array_like` must be one of `Vector` or `Arrow::Array` or `Array`.
486
+ # @return [DataFrame]
487
+ # assigned DataFrame.
488
+ # @example Assign new data for a existing column by block
489
+ # comecome.assign(:age) { age + 29 }
490
+ #
491
+ # # =>
492
+ # #<RedAmber::DataFrame : 3 x 2 Vectors, 0x000000000007af94>
493
+ # name age
494
+ # <string> <uint8>
495
+ # 0 Yasuko 97
496
+ # 1 Rui 78
497
+ # 2 Hinata 57
498
+ #
499
+ # @example Assign multiple data
500
+ # comecome.assign(:age_in_1993, :brother) do
501
+ # [
502
+ # age + 29,
503
+ # ['Santa', nil, 'Momotaro'],
504
+ # ]
505
+ # end
506
+ #
507
+ # # =>
508
+ # #<RedAmber::DataFrame : 3 x 4 Vectors, 0x00000000000b363c>
509
+ # name age age_in_1993 brother
510
+ # <string> <uint8> <uint8> <string>
511
+ # 0 Yasuko 68 97 Santa
512
+ # 1 Rui 49 78 (nil)
513
+ # 2 Hinata 28 57 Momotaro
514
+ #
166
515
  def assign(*assigner, &block)
167
516
  assign_update(*assigner, append_to_left: false, &block)
168
517
  end
169
518
 
519
+ # Assign new or updated variables (columns) and create an updated DataFrame.
520
+ # - Array-like variables with new keys will append new columns from left.
521
+ # - Array-like variables with exisiting keys will update corresponding vectors.
522
+ # - Symbol key and String key are considered as the same key.
523
+ # - If assigner is empty or nil, returns self.
524
+ #
525
+ # @overload assign_left(key_value_pairs)
526
+ # accepts pairs of key and values by an Array or a Hash.
527
+ #
528
+ # @param key_value_pairs [Array<key, array_like>, Hash{key => array_like}]
529
+ # `key` must be a Symbol or a String.
530
+ # `array_like` is column data to be assigned.
531
+ # It must be one of `Vector` or `Arrow::Array` or `Array`.
532
+ # @return [DataFrame]
533
+ # assigned DataFrame.
534
+ # @example Assign a new column from left
535
+ # df
536
+ #
537
+ # # =>
538
+ # #<RedAmber::DataFrame : 5 x 3 Vectors, 0x000000000000c10c>
539
+ # index float string
540
+ # <uint8> <double> <string>
541
+ # 0 0 0.0 A
542
+ # 1 1 1.1 B
543
+ # 2 2 2.2 C
544
+ # 3 3 NaN D
545
+ # 4 (nil) (nil) (nil)
546
+ #
547
+ # df.assign_left(new_index: df.indices(1))
548
+ #
549
+ # # =>
550
+ # #<RedAmber::DataFrame : 5 x 4 Vectors, 0x000000000001787c>
551
+ # new_index index float string
552
+ # <uint8> <uint8> <double> <string>
553
+ # 0 1 0 0.0 A
554
+ # 1 2 1 1.1 B
555
+ # 2 3 2 2.2 C
556
+ # 3 4 3 NaN D
557
+ # 4 5 (nil) (nil) (nil)
558
+ #
559
+ # @overload assign_left
560
+ # accepts block yielding pairs of key and values.
561
+ #
562
+ # @yield [self]
563
+ # the block is called within the context of self.
564
+ # (Block is called by instance_eval(&block). )
565
+ # @yieldreturn [Array<key, array_like>, Hash(key => array_like)]
566
+ # `key` must be a Symbol or a String.
567
+ # `array_like` is column data to be assigned.
568
+ # It must be one of `Vector` or `Arrow::Array` or `Array`.
569
+ # @return [DataFrame]
570
+ # assigned DataFrame.
571
+ #
572
+ # @overload assign_left(keys)
573
+ # accepts keys from argument and pairs of key and values from block.
574
+ #
575
+ # @param keys [Symbol, String]
576
+ # keys of columns to create or update.
577
+ # @yield [self]
578
+ # the block is called within the context of self.
579
+ # (Block is called by instance_eval(&block).)
580
+ # @yieldreturn [Array<array_like>]
581
+ # column data to be assigned.
582
+ # `array_like` must be one of `Vector` or `Arrow::Array` or `Array`.
583
+ # @return [DataFrame]
584
+ # assigned DataFrame.
585
+ #
170
586
  def assign_left(*assigner, &block)
171
587
  assign_update(*assigner, append_to_left: true, &block)
172
588
  end
@@ -234,6 +650,7 @@ module RedAmber
234
650
  unless not_existing_keys.empty?
235
651
  raise DataFrameArgumentError, "Not existing: #{not_existing_keys}"
236
652
  end
653
+ return self if key_pairs.all? { |k, v| k == v }
237
654
 
238
655
  fields =
239
656
  keys.map do |key|