carray 3.0.5 → 3.0.6

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_axis_dispatch.c +6 -0
  6. data/ext/ca_axis_group.c +1 -1
  7. data/ext/ca_categorical_iterator.c +7 -7
  8. data/ext/ca_kernel_iterator.c +116 -231
  9. data/ext/ca_kernel_iterator.h +410 -254
  10. data/ext/ca_obj_bincmp.c +9 -1
  11. data/ext/ca_obj_binop.c +10 -1
  12. data/ext/ca_obj_bitfield.c +2 -0
  13. data/ext/ca_obj_byte_swap.c +2 -0
  14. data/ext/ca_obj_face.c +2 -0
  15. data/ext/ca_obj_fake.c +3 -1
  16. data/ext/ca_obj_grid.c +167 -12
  17. data/ext/ca_obj_meld.c +69 -113
  18. data/ext/ca_obj_moncmp.c +9 -1
  19. data/ext/ca_obj_monop.c +40 -2
  20. data/ext/ca_obj_object.c +54 -20
  21. data/ext/ca_obj_reduce.c +15 -1
  22. data/ext/ca_obj_refer.c +65 -20
  23. data/ext/ca_obj_remap.c +69 -10
  24. data/ext/ca_obj_roll.c +1 -0
  25. data/ext/ca_obj_select.c +4 -1
  26. data/ext/ca_obj_select_axis.c +1 -0
  27. data/ext/ca_obj_shift.c +1 -0
  28. data/ext/ca_obj_stack.c +310 -111
  29. data/ext/ca_obj_stride.c +131 -45
  30. data/ext/ca_obj_string.c +18 -2
  31. data/ext/ca_obj_tile.c +1 -0
  32. data/ext/ca_obj_triop.c +9 -1
  33. data/ext/ca_obj_window.c +1 -0
  34. data/ext/ca_op_ipower.c +8 -8
  35. data/ext/caframe_csv_reader.c +1 -1
  36. data/ext/carray.h +2 -1
  37. data/ext/carray_access.c +27 -64
  38. data/ext/carray_address_basis.c +134 -0
  39. data/ext/carray_attribute.c +8 -0
  40. data/ext/carray_broadcast.c +3 -4
  41. data/ext/carray_build_flags.h +1 -1
  42. data/ext/carray_cast.c +8 -11
  43. data/ext/carray_conversion.c +9 -5
  44. data/ext/carray_core.c +811 -72
  45. data/ext/carray_element.c +13 -9
  46. data/ext/carray_histogram.c +25 -3
  47. data/ext/carray_internal.h +147 -12
  48. data/ext/carray_kernels_bincmp.c +2007 -3
  49. data/ext/carray_kernels_binop.c +25365 -5199
  50. data/ext/carray_kernels_init.c +8 -8
  51. data/ext/carray_kernels_monop.c +10696 -3678
  52. data/ext/carray_kernels_reduce_aggregate.c +4429 -702
  53. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  54. data/ext/carray_kernels_reduce_cumulative.c +1808 -248
  55. data/ext/carray_kernels_reduce_extreme.c +4508 -511
  56. data/ext/carray_kernels_reduce_variance.c +2284 -152
  57. data/ext/carray_kernels_scan.c +240 -240
  58. data/ext/carray_kernels_search.c +4211 -264
  59. data/ext/carray_kernels_sort.c +367 -450
  60. data/ext/carray_lazy.c +47 -0
  61. data/ext/carray_loop.c +1 -1
  62. data/ext/carray_mask.c +130 -139
  63. data/ext/carray_median_percentile.c +4 -4
  64. data/ext/carray_memory_view.c +13 -7
  65. data/ext/carray_operator.c +147 -44
  66. data/ext/carray_order.c +3 -2
  67. data/ext/carray_random.c +4 -1
  68. data/ext/carray_slab.c +97 -21
  69. data/ext/carray_sort.c +1 -1
  70. data/ext/carray_test.c +74 -19
  71. data/ext/carray_time_text.c +2 -2
  72. data/ext/extconf.rb +11 -0
  73. data/ext/mkkernel.rb +346 -94
  74. data/ext/ruby_carray.c +4 -1
  75. data/ext/version.h +4 -4
  76. data/lib/carray/attribute.rb +25 -1
  77. data/lib/carray/autoload_carray.rb +3 -3
  78. data/lib/carray/autoload_method_extension.rb +5 -3
  79. data/lib/carray/axis_group.rb +13 -7
  80. data/lib/carray/basics.rb +1 -1
  81. data/lib/carray/block_iterator.rb +17 -3
  82. data/lib/carray/categorical.rb +6 -8
  83. data/lib/carray/categorical_iterator.rb +2 -2
  84. data/lib/carray/complex.rb +2 -2
  85. data/lib/carray/conditional.rb +3 -2
  86. data/lib/carray/frame/column_verbs.rb +45 -14
  87. data/lib/carray/frame/convert.rb +19 -0
  88. data/lib/carray/frame/csv_parser.rb +32 -32
  89. data/lib/carray/frame/describe.rb +4 -2
  90. data/lib/carray/frame/frame.rb +399 -78
  91. data/lib/carray/frame/group.rb +679 -53
  92. data/lib/carray/frame/io.rb +209 -71
  93. data/lib/carray/frame/join.rb +146 -37
  94. data/lib/carray/frame/records.rb +3 -3
  95. data/lib/carray/frame/reshape.rb +89 -11
  96. data/lib/carray/frame/sort.rb +57 -9
  97. data/lib/carray/frame/verbs.rb +384 -49
  98. data/lib/carray/frame/windows.rb +44 -6
  99. data/lib/carray/inspect.rb +7 -2
  100. data/lib/carray/iterator.rb +32 -0
  101. data/lib/carray/lazy.rb +17 -8
  102. data/lib/carray/mask_gap_fill.rb +37 -17
  103. data/lib/carray/meld_reduce.rb +1 -1
  104. data/lib/carray/methods/choose.rb +3 -2
  105. data/lib/carray/methods/meshgrid.rb +3 -1
  106. data/lib/carray/runtime.rb +47 -8
  107. data/lib/carray/segment_iterator.rb +23 -8
  108. data/lib/carray/serialize.rb +34 -17
  109. data/lib/carray/stack.rb +18 -6
  110. data/lib/carray/time.rb +5 -0
  111. data/lib/carray/window_iterator.rb +16 -4
  112. data/yard-stubs/ca_obj_array.rb +26 -1
  113. data/yard-stubs/carray_cast.rb +8 -5
  114. data/yard-stubs/carray_conversion.rb +8 -2
  115. data/yard-stubs/carray_mask.rb +20 -1
  116. data/yard-stubs/carray_math.rb +31 -16
  117. data/yard-stubs/carray_memory_view.rb +7 -0
  118. data/yard-stubs/carray_test.rb +7 -4
  119. metadata +18 -4
@@ -1,4 +1,4 @@
1
- # A small, self-contained CSV tokenizer for CAFrame.from_csv.
1
+ # A small, self-contained CSV tokenizer for CAFrame.read_csv.
2
2
  #
3
3
  # It produces raw String cells and does no type inference — casting is a
4
4
  # separate step. The tokenizer is tuned for the
@@ -22,14 +22,14 @@
22
22
  # Two entry points share the Tokenizer:
23
23
  # * CSVParser.parse / parse_file -- whole-file parse into [headers, rows].
24
24
  # * CSVReader -- the block reading-control DSL used by
25
- # CAFrame.from_csv (header / skip / body /
25
+ # CAFrame.read_csv (header / skip / body /
26
26
  # column_names).
27
27
 
28
28
  require "strscan"
29
29
  require "stringio"
30
30
 
31
31
  class CAFrame
32
- # CSV tokenizer behind `CAFrame.from_csv`. It produces raw String cells
32
+ # CSV tokenizer behind `CAFrame.read_csv`. It produces raw String cells
33
33
  # and does no type inference — casting is a separate step.
34
34
  #
35
35
  # Records with no quote character take a `String#split` fast path; only
@@ -79,7 +79,7 @@ class CAFrame
79
79
 
80
80
  # Parse an IO (or anything answering +gets+). The first record supplies the
81
81
  # headers; the rest are data rows. Blank lines are skipped, including in a
82
- # one-column file (from_csv reads them there as missing cells).
82
+ # one-column file (read_csv reads them there as missing cells).
83
83
  def parse(io, sep: ",", quote: '"', strip: false)
84
84
  tok = Tokenizer.new(sep, quote, strip)
85
85
  headers = nil
@@ -315,24 +315,24 @@ class CAFrame
315
315
  end
316
316
  end
317
317
 
318
- # The reading-control block for CAFrame.from_csv. A file often has
318
+ # The reading-control block for CAFrame.read_csv. A file often has
319
319
  # preamble lines, a units row, or no header at all; the block is given the
320
320
  # reader and says, in order, how to consume the stream:
321
321
  #
322
- # CAFrame.from_csv(path) do |r|
322
+ # CAFrame.read_csv(path) do |r|
323
323
  # r.skip 3 # drop 3 preamble lines
324
324
  # r.header # next record supplies the column names
325
325
  # r.skip 1 # drop a units row
326
326
  # r.data # the rest are data rows
327
327
  # end
328
328
  #
329
- # CAFrame.from_csv(path) { it.column_names "date", "temp", "rh"; it.data }
329
+ # CAFrame.read_csv(path) { it.column_names "date", "temp", "rh"; it.data }
330
330
  #
331
331
  # The verbs are +skip+ / +header+ / +column_names+ / +columns+ / +data+;
332
332
  # each returns a value useful inline (header returns its fields) and the ordering is the
333
333
  # script. The reader is a parameter, not self, so a local variable named
334
334
  # +data+ or +header+ cannot stand in for the verb. Without a block,
335
- # from_csv reads by its header: / data: / column_names: (see +layout+).
335
+ # read_csv reads by its header: / data: / column_names: (see +layout+).
336
336
  class CSVReader
337
337
  def initialize(io, sep: ",", quote: '"', strip: false, read_text_as_const_string: false)
338
338
  @read_text_as_const_string = read_text_as_const_string
@@ -388,7 +388,7 @@ class CAFrame
388
388
  key = name.to_s
389
389
  @named_headers ||= {}
390
390
  if @named_headers.key?(key)
391
- raise ArgumentError, "from_csv: header(#{name.inspect}) is read twice"
391
+ raise ArgumentError, "read_csv: header(#{name.inspect}) is read twice"
392
392
  end
393
393
  @named_headers[key] = [fields, @tok.record_line, name]
394
394
  end
@@ -403,7 +403,7 @@ class CAFrame
403
403
  def column_names(*names)
404
404
  names = names.flatten.map(&:to_s)
405
405
  if names.include?("")
406
- raise ArgumentError, "from_csv: column_names: a column name can not be empty"
406
+ raise ArgumentError, "read_csv: column_names: a column name can not be empty"
407
407
  end
408
408
  if @header_line
409
409
  check_column_names(names, @names.size, @header_line)
@@ -431,15 +431,15 @@ class CAFrame
431
431
  end
432
432
  selection = selection.flatten
433
433
  if selection.empty?
434
- raise ArgumentError, "from_csv: columns: names no column"
434
+ raise ArgumentError, "read_csv: columns: names no column"
435
435
  end
436
436
  selection.each do |c|
437
437
  next if c.is_a?(String)
438
- CAFrame.__send__(:column_key, c, "from_csv", "columns: a column name") if c.is_a?(Symbol)
438
+ CAFrame.__send__(:column_key, c, "read_csv", "columns: a column name") if c.is_a?(Symbol)
439
439
  next if c.is_a?(Integer) && c >= 0
440
440
  next if c.is_a?(Range) && (c.begin.nil? || (c.begin.is_a?(Integer) && c.begin >= 0)) &&
441
441
  (c.end.nil? || c.end.is_a?(Integer))
442
- raise ArgumentError, "from_csv: columns: takes column names, or indexes from 0 " \
442
+ raise ArgumentError, "read_csv: columns: takes column names, or indexes from 0 " \
443
443
  "and Ranges of them (got #{c.inspect})"
444
444
  end
445
445
  @selection = selection
@@ -450,17 +450,17 @@ class CAFrame
450
450
  private def column_renames(map)
451
451
  map.each do |key, name|
452
452
  if key.is_a?(Range)
453
- raise ArgumentError, "from_csv: columns: a Hash selects one column per key, " \
453
+ raise ArgumentError, "read_csv: columns: a Hash selects one column per key, " \
454
454
  "not a Range (got #{key.inspect})"
455
455
  end
456
- CAFrame.__send__(:column_key, name, "from_csv", "columns: a column name",
456
+ CAFrame.__send__(:column_key, name, "read_csv", "columns: a column name",
457
457
  hint: "given for #{key.inspect}")
458
458
  if name.empty?
459
- raise ArgumentError, "from_csv: columns: a column name can not be empty " \
459
+ raise ArgumentError, "read_csv: columns: a column name can not be empty " \
460
460
  "(given for #{key.inspect})"
461
461
  end
462
462
  end
463
- CAFrame.__send__(:refuse_repeated_names, map.values, "from_csv", "columns: names")
463
+ CAFrame.__send__(:refuse_repeated_names, map.values, "read_csv", "columns: names")
464
464
  map.values
465
465
  end
466
466
 
@@ -474,7 +474,7 @@ class CAFrame
474
474
  # Without a header the columns are named c0, c1, ... by position.
475
475
  j = names ? names.index(c) : c[/\Ac(0|[1-9]\d*)\z/, 1]&.to_i
476
476
  if j.nil?
477
- raise KeyError, "from_csv: columns: names no column #{c.inspect}" +
477
+ raise KeyError, "read_csv: columns: names no column #{c.inspect}" +
478
478
  (names ? "" : " (with no header the columns are c0, c1, ...)")
479
479
  end
480
480
  j
@@ -488,9 +488,9 @@ class CAFrame
488
488
  (first..last).to_a
489
489
  end
490
490
  end
491
- CAFrame.__send__(:refuse_repeated_names, indexes, "from_csv", "columns: selects column")
491
+ CAFrame.__send__(:refuse_repeated_names, indexes, "read_csv", "columns: selects column")
492
492
  if ncol && (bad = indexes.find { |j| j >= ncol })
493
- raise ArgumentError, "from_csv: columns: #{bad} is past the #{ncol} columns of the file"
493
+ raise ArgumentError, "read_csv: columns: #{bad} is past the #{ncol} columns of the file"
494
494
  end
495
495
  indexes
496
496
  end
@@ -503,7 +503,7 @@ class CAFrame
503
503
 
504
504
  private def check_column_names(names, ncol, line)
505
505
  return if names.size == ncol
506
- raise ArgumentError, "from_csv: column_names: gives #{names.size} " \
506
+ raise ArgumentError, "read_csv: column_names: gives #{names.size} " \
507
507
  "name#{names.size == 1 ? '' : 's'} for the #{ncol} columns of line #{line}"
508
508
  end
509
509
 
@@ -525,7 +525,7 @@ class CAFrame
525
525
  read_data(nil)
526
526
  end
527
527
 
528
- # The header: / data: / column_names: of from_csv, as the verbs above.
528
+ # The header: / data: / column_names: of read_csv, as the verbs above.
529
529
  # They index the lines of the file from 0, as File.readlines does (an
530
530
  # error names a line from 1, as an editor does). header: defaults to 0,
531
531
  # or to none (false) when column_names: is given; data: is the first line
@@ -534,12 +534,12 @@ class CAFrame
534
534
  def layout(header: nil, data: nil, column_names: nil, columns: nil, attrs: nil)
535
535
  header = column_names ? false : 0 if header.nil?
536
536
  unless header == false || (header.is_a?(Integer) && header >= 0)
537
- raise ArgumentError, "from_csv: header: takes the index of a line, from 0, " \
537
+ raise ArgumentError, "read_csv: header: takes the index of a line, from 0, " \
538
538
  "or false (got #{header.inspect})"
539
539
  end
540
- attr_keys = attrs.nil? ? [] : CAFrame.send(:csv_attr_keys, attrs, "from_csv")
540
+ attr_keys = attrs.nil? ? [] : CAFrame.send(:csv_attr_keys, attrs, "read_csv")
541
541
  if !header && !attr_keys.empty?
542
- raise ArgumentError, "from_csv: attrs: reads the lines below the header, " \
542
+ raise ArgumentError, "read_csv: attrs: reads the lines below the header, " \
543
543
  "so it needs a header line"
544
544
  end
545
545
  first, last = CSVReader.data_lines(data)
@@ -552,7 +552,7 @@ class CAFrame
552
552
  self.columns(*(columns.is_a?(Array) ? columns : [columns])) if columns
553
553
  first ||= @tok.lineno
554
554
  if first < @tok.lineno
555
- raise ArgumentError, "from_csv: data: #{first} does not come after header: #{header}" +
555
+ raise ArgumentError, "read_csv: data: #{first} does not come after header: #{header}" +
556
556
  (attr_keys.empty? ? "" : " and the #{attr_keys.size} attrs: lines")
557
557
  end
558
558
  skip(first - @tok.lineno)
@@ -570,15 +570,15 @@ class CAFrame
570
570
  e -= 1 if e.is_a?(Integer) && spec.exclude_end?
571
571
  [spec.begin, e]
572
572
  else
573
- raise ArgumentError, "from_csv: data: takes the index of a line or a Range of " \
573
+ raise ArgumentError, "read_csv: data: takes the index of a line or a Range of " \
574
574
  "them, from 0 (got #{spec.inspect})"
575
575
  end
576
576
  [first, last].each do |n|
577
577
  next if n.nil? || (n.is_a?(Integer) && n >= 0)
578
- raise ArgumentError, "from_csv: data: lines are indexed from 0 (got #{spec.inspect})"
578
+ raise ArgumentError, "read_csv: data: lines are indexed from 0 (got #{spec.inspect})"
579
579
  end
580
580
  if first && last && last < first
581
- raise ArgumentError, "from_csv: data: #{spec.inspect} ends before it starts"
581
+ raise ArgumentError, "read_csv: data: #{spec.inspect} ends before it starts"
582
582
  end
583
583
  [first, last]
584
584
  end
@@ -648,7 +648,7 @@ class CAFrame
648
648
  end
649
649
 
650
650
  # The columns read_body_into_const_string_columns made, as +result+
651
- # hands them to CAFrame.from_csv.
651
+ # hands them to CAFrame.read_csv.
652
652
  ConstStringColumns = Struct.new(:columns)
653
653
 
654
654
  private def body_in_c(last = nil, chunk_bytes = CHUNK_BYTES)
@@ -767,7 +767,7 @@ class CAFrame
767
767
  attrs = indexes.map { {} }
768
768
  @named_headers.each do |key, (fields, line, name)|
769
769
  if fields.size > ncol
770
- raise ArgumentError, "from_csv: header(#{name.inspect}) on line #{line} " \
770
+ raise ArgumentError, "read_csv: header(#{name.inspect}) on line #{line} " \
771
771
  "has #{fields.size} fields for the #{ncol} columns"
772
772
  end
773
773
  indexes.each_with_index do |j, k|
@@ -784,7 +784,7 @@ class CAFrame
784
784
  @names ? @names.size : ((@rows || []).map(&:size).max || 0)
785
785
  end
786
786
 
787
- # [names_or_nil, rows] for CAFrame.from_csv to build from. names is nil when
787
+ # [names_or_nil, rows] for CAFrame.read_csv to build from. names is nil when
788
788
  # neither header nor column_names ran (positional names are generated).
789
789
  def result
790
790
  return table_result if @table
@@ -30,10 +30,12 @@ class CAFrame
30
30
  # column with no present cell has UNDEF statistics. The index is not a
31
31
  # column and is not summarized.
32
32
  #
33
- # @param names [Array<String>] the columns to summarize; default all.
33
+ # @param names [Array<String, Range, Regexp, Symbol>] the columns to
34
+ # summarize, picked as +select+ picks them (a Symbol is a kind of column,
35
+ # such as :numeric); default all.
34
36
  # @return [CAFrame] a new frame with one row per column.
35
37
  def describe(*names)
36
- names = names.empty? ? column_names : column_keys(names, "describe")
38
+ names = names.empty? ? column_names : names_selected(names, "describe")
37
39
  rows = names.map { |name| describe_column(self[name]) }
38
40
  stats = %w[type count masked unique min max mean stddev]
39
41
  cols = stats.to_h do |stat|