carray 3.0.0 → 3.0.2
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.
- checksums.yaml +4 -4
- data/.yardopts +2 -1
- data/CHANGELOG.md +845 -3
- data/{NEWS.md → CHANGELOG.v1.md} +1 -1
- data/README.md +33 -33
- data/carray.gemspec +12 -8
- data/ext/ca_axis_dispatch.c +33 -4
- data/ext/ca_axis_group.c +256 -114
- data/ext/ca_binop_dispatch.c +5 -6
- data/ext/ca_binop_dispatch.h +0 -7
- data/ext/ca_categorical_iterator.c +121 -67
- data/ext/ca_for_each_element.h +6 -8
- data/ext/ca_group_iter.c +1 -1
- data/ext/ca_kernel_iterator.c +386 -74
- data/ext/ca_kernel_iterator.h +145 -45
- data/ext/ca_obj_array.c +62 -20
- data/ext/ca_obj_bincmp.c +57 -27
- data/ext/ca_obj_binop.c +86 -30
- data/ext/ca_obj_block.c +4 -4
- data/ext/ca_obj_const_string.c +88 -27
- data/ext/ca_obj_face.c +104 -0
- data/ext/ca_obj_face.h +48 -1
- data/ext/ca_obj_fake.c +11 -0
- data/ext/ca_obj_fixlen_string.c +21 -6
- data/ext/ca_obj_grid.c +11 -3
- data/ext/ca_obj_meld.c +125 -22
- data/ext/ca_obj_moncmp.c +33 -14
- data/ext/ca_obj_monop.c +19 -7
- data/ext/ca_obj_object.c +107 -4
- data/ext/ca_obj_record.c +3 -1
- data/ext/ca_obj_refer.c +30 -6
- data/ext/ca_obj_roll.c +13 -5
- data/ext/ca_obj_select.c +49 -34
- data/ext/ca_obj_select_axis.c +16 -6
- data/ext/ca_obj_shift.c +3 -3
- data/ext/ca_obj_stack.c +3 -8
- data/ext/ca_obj_stride.c +178 -24
- data/ext/ca_obj_string.c +11 -5
- data/ext/ca_obj_tile.c +13 -5
- data/ext/ca_obj_time.c +3 -1
- data/ext/ca_obj_timedelta.c +3 -1
- data/ext/ca_obj_transpose.c +1 -1
- data/ext/ca_obj_triop.c +62 -23
- data/ext/ca_obj_window.c +94 -19
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +1 -5
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +5 -5
- data/ext/ca_sweep_engine.c +362 -155
- data/ext/ca_sweep_engine.h +39 -9
- data/ext/ca_transform_common.c +7 -0
- data/ext/carray.h +148 -132
- data/ext/carray_access.c +83 -49
- data/ext/carray_address_basis.c +590 -0
- data/ext/carray_bincount.c +8 -8
- data/ext/carray_broadcast.c +102 -9
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2010 -335
- data/ext/carray_call_cfunc.h +153 -0
- data/ext/carray_cast.c +184 -72
- data/ext/carray_conversion.c +22 -22
- data/ext/carray_copy.c +67 -32
- data/ext/carray_core.c +140 -13
- data/ext/carray_count.c +10 -11
- data/ext/carray_element.c +7 -7
- data/ext/carray_factorize.c +72 -51
- data/ext/carray_hold.c +1 -1
- data/ext/carray_index_classifier.c +7 -21
- data/ext/carray_internal.h +36 -3
- data/ext/carray_kernels_bincmp.c +1 -0
- data/ext/carray_kernels_binop.c +320 -298
- data/ext/carray_kernels_init.c +1095 -0
- data/ext/carray_kernels_map.c +1 -0
- data/ext/carray_kernels_moncmp.c +1 -0
- data/ext/carray_kernels_monop.c +412 -411
- data/ext/carray_kernels_reduce_aggregate.c +256 -87
- data/ext/carray_kernels_reduce_boolean.c +1 -0
- data/ext/carray_kernels_reduce_cumulative.c +324 -54
- data/ext/carray_kernels_reduce_extreme.c +605 -58
- data/ext/carray_kernels_reduce_variance.c +1 -0
- data/ext/carray_kernels_scan.c +5 -4
- data/ext/carray_kernels_search.c +187 -106
- data/ext/carray_kernels_sort.c +1 -0
- data/ext/carray_kernels_triop.c +1 -0
- data/ext/carray_lazy.c +175 -5
- data/ext/carray_loop.c +7 -1
- data/ext/carray_mask.c +29 -13
- data/ext/carray_median_percentile.c +56 -1
- data/ext/carray_memory_view.c +46 -21
- data/ext/carray_operator.c +50 -53
- data/ext/carray_order.c +1 -1
- data/ext/carray_partition.c +4 -4
- data/ext/carray_random.c +391 -47
- data/ext/carray_scatter.c +1 -1
- data/ext/carray_slab.c +13 -0
- data/ext/carray_sort.c +22 -24
- data/ext/carray_sort_kernel.c +10 -10
- data/ext/carray_test.c +1 -1
- data/ext/extconf.rb +18 -0
- data/ext/mk_call_cfunc.rb +323 -145
- data/ext/mkkernel.rb +651 -129
- data/ext/ruby_carray.c +28 -17
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +14 -1
- data/lib/carray/axis_group.rb +84 -7
- data/lib/carray/basics.rb +63 -59
- data/lib/carray/bincount_nd.rb +12 -12
- data/lib/carray/block_iterator.rb +111 -25
- data/lib/carray/categorical.rb +154 -37
- data/lib/carray/categorical_iterator.rb +300 -151
- data/lib/carray/conditional.rb +14 -14
- data/lib/carray/const_string.rb +131 -27
- data/lib/carray/construct.rb +61 -0
- data/lib/carray/core_extensions.rb +62 -5
- data/lib/carray/data_type_extension.rb +21 -1
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +3 -3
- data/lib/carray/frame/convert.rb +1 -1
- data/lib/carray/frame/csv_parser.rb +11 -4
- data/lib/carray/frame/frame.rb +85 -21
- data/lib/carray/frame/group.rb +36 -3
- data/lib/carray/frame/io.rb +224 -15
- data/lib/carray/frame/records.rb +18 -4
- data/lib/carray/frame/sort.rb +1 -1
- data/lib/carray/frame/verbs.rb +32 -12
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +16 -16
- data/lib/carray/inspect.rb +43 -16
- data/lib/carray/iterator.rb +147 -3
- data/lib/carray/lazy.rb +88 -73
- data/lib/carray/mask_gap_fill.rb +3 -1
- data/lib/carray/meld_reduce.rb +2 -2
- data/lib/carray/methods/align_addr.rb +1 -1
- data/lib/carray/methods/composition.rb +1 -1
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/is_in.rb +25 -14
- data/lib/carray/methods/locate_addr.rb +81 -3
- data/lib/carray/methods/mask_duplicates.rb +36 -2
- data/lib/carray/methods/meshgrid.rb +4 -5
- data/lib/carray/methods/mode.rb +2 -2
- data/lib/carray/methods/nunique.rb +23 -2
- data/lib/carray/methods/repeat.rb +110 -0
- data/lib/carray/methods/snap.rb +7 -2
- data/lib/carray/methods/unique.rb +44 -4
- data/lib/carray/methods/value_counts.rb +2 -2
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +0 -19
- data/lib/carray/slab_iterator.rb +77 -19
- data/lib/carray/string_operation_extension.rb +10 -6
- data/lib/carray/time.rb +906 -491
- data/lib/carray/window_iterator.rb +421 -27
- data/lib/carray.rb +7 -5
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +68 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +57 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +34 -0
- data/yard-stubs/carray_count.rb +84 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +228 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +1615 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +103 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +252 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +59 -0
- data/yard-stubs/carray_sort.rb +165 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +67 -10
- data/ext/ca_obj_unbound_repeat.c +0 -496
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "carray/methods/discovery_along"
|
|
2
|
+
|
|
1
3
|
class CArray
|
|
2
4
|
|
|
3
5
|
# @overload mask_duplicates(axis: nil)
|
|
@@ -23,14 +25,46 @@ class CArray
|
|
|
23
25
|
# `nil` uses flatten order.
|
|
24
26
|
# @return [CArray] shape-preserving copy of `self` with
|
|
25
27
|
# duplicates masked.
|
|
26
|
-
|
|
28
|
+
# @overload mask_duplicates(along: k)
|
|
29
|
+
# Returns a shape-preserving copy of `self` with every cell of a
|
|
30
|
+
# duplicated **sub-array** masked, comparing whole sub-arrays rather
|
|
31
|
+
# than cells: `along: k` names the axis whose index enumerates them,
|
|
32
|
+
# so `z.mask_duplicates(along: 0)` masks each repeated row of a 2-D
|
|
33
|
+
# array and keeps the first occurrence.
|
|
34
|
+
#
|
|
35
|
+
# Note the contrast with `axis:`, which names the axis a *fiber runs
|
|
36
|
+
# along* and marks repeated values inside each fiber. The two cannot
|
|
37
|
+
# be given together.
|
|
38
|
+
#
|
|
39
|
+
# A sub-array holding a masked cell does not participate: it is
|
|
40
|
+
# neither judged a duplicate nor able to make a later one, and its
|
|
41
|
+
# cells keep the mask they came with. `object` arrays are refused,
|
|
42
|
+
# because their cells hold Ruby references.
|
|
43
|
+
#
|
|
44
|
+
# @param along [Integer] axis whose index enumerates the sub-arrays.
|
|
45
|
+
# @return [CArray] shape-preserving copy of `self` with duplicated
|
|
46
|
+
# sub-arrays masked.
|
|
47
|
+
def mask_duplicates (axis: nil, along: nil)
|
|
48
|
+
reject_axis_with_along(axis, along, "mask_duplicates")
|
|
49
|
+
if along
|
|
50
|
+
keys = fibers_as_cells(along, "mask_duplicates")
|
|
51
|
+
# A sub-array that did not participate (it held a masked cell) is
|
|
52
|
+
# masked in `keys` already; only the ones that did and repeated
|
|
53
|
+
# are duplicates here.
|
|
54
|
+
repeated = keys.mask_duplicates.is_masked & keys.is_not_masked
|
|
55
|
+
args = [:_] * ndim
|
|
56
|
+
args[normalize_axis(along, "mask_duplicates")] = nil
|
|
57
|
+
spread = CArray.boolean(*shape)
|
|
58
|
+
spread[] = repeated[*args]
|
|
59
|
+
return mask_where(spread)
|
|
60
|
+
end
|
|
27
61
|
dup =
|
|
28
62
|
if axis.nil?
|
|
29
63
|
# One seen-set over the flattened array, then restore shape.
|
|
30
64
|
flatten.send(:__mask_duplicates__, 0).reshape(*shape)
|
|
31
65
|
else
|
|
32
66
|
# Per-fiber single-pass seen-set hash (C __mask_duplicates__): one lane
|
|
33
|
-
# per
|
|
67
|
+
# per data type family (integer widen, float bitwise key with NaN collapse,
|
|
34
68
|
# object rb_hash + rb_eql, fixlen byte-hash + memcmp, boolean via the
|
|
35
69
|
# uint8 lane). O(distinct) memory, no sort/gather/scatter buffers.
|
|
36
70
|
__mask_duplicates__(normalize_axis(axis, "mask_duplicates"))
|
|
@@ -17,10 +17,9 @@ class CArray
|
|
|
17
17
|
# input order in both modes.
|
|
18
18
|
#
|
|
19
19
|
# When `copy` is true (default) each output is a materialised
|
|
20
|
-
# CArray; when false, view
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# for large grids.
|
|
20
|
+
# CArray; when false, a view is returned. When `sparse` is true
|
|
21
|
+
# each output keeps a size-1 axis wherever the full grid would
|
|
22
|
+
# repeat, and broadcasts on demand, saving memory for large grids.
|
|
24
23
|
#
|
|
25
24
|
# If a block is given, yields the resulting arrays as splat
|
|
26
25
|
# arguments and returns the block's value.
|
|
@@ -77,7 +76,7 @@ class CArray
|
|
|
77
76
|
list = axes.map.with_index do |axis, k|
|
|
78
77
|
d = dest[k]
|
|
79
78
|
idx = if sparse
|
|
80
|
-
Array.new(ndim) { |i| i == d ? nil :
|
|
79
|
+
Array.new(ndim) { |i| i == d ? nil : :_ }
|
|
81
80
|
else
|
|
82
81
|
out_shape.dup.tap { |s| s[d] = :% }
|
|
83
82
|
end
|
data/lib/carray/methods/mode.rb
CHANGED
|
@@ -33,7 +33,7 @@ class CArray
|
|
|
33
33
|
# the whole array.
|
|
34
34
|
# @return [CArray] boolean CArray of `self.shape`.
|
|
35
35
|
def is_mode (axis: nil)
|
|
36
|
-
# Per-fiber two-pass frequency table (C __is_mode__), one lane per
|
|
36
|
+
# Per-fiber two-pass frequency table (C __is_mode__), one lane per data type
|
|
37
37
|
# family (numeric widen / NaN collapse, object rb_hash + rb_eql, fixlen
|
|
38
38
|
# byte-hash + memcmp). Ties are all marked; masked cells stay false.
|
|
39
39
|
if axis.nil?
|
|
@@ -112,7 +112,7 @@ class CArray
|
|
|
112
112
|
|
|
113
113
|
private
|
|
114
114
|
|
|
115
|
-
# Flat mode: the distinct modal values ascending, 1-D CArray of self's
|
|
115
|
+
# Flat mode: the distinct modal values ascending, 1-D CArray of self's data type.
|
|
116
116
|
# The single source of what counts as a mode (per-axis reuses it per fiber).
|
|
117
117
|
# The distinct values with the maximum count, read from the frequency table
|
|
118
118
|
# (value_counts, which covers numeric / object / fixlen), then sorted
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "carray/methods/discovery_along"
|
|
2
|
+
|
|
1
3
|
class CArray
|
|
2
4
|
|
|
3
5
|
# @overload nunique(axis: nil, keep_axis: false)
|
|
@@ -29,8 +31,27 @@ class CArray
|
|
|
29
31
|
# axis as a length-1 axis instead of dropping it.
|
|
30
32
|
# @return [Integer, CArray] Integer for `axis: nil`, otherwise a
|
|
31
33
|
# reduced `CA_INT64` CArray.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
# @overload nunique(along: k)
|
|
35
|
+
# Returns how many distinct **sub-arrays** `self` holds, comparing
|
|
36
|
+
# whole sub-arrays rather than cells: `along: k` names the axis
|
|
37
|
+
# whose index enumerates them, so `z.nunique(along: 0)` counts the
|
|
38
|
+
# distinct rows of a 2-D array. This is `len(np.unique(z, axis=k))`.
|
|
39
|
+
#
|
|
40
|
+
# Note the contrast with `axis:`, which names the axis a *fiber runs
|
|
41
|
+
# along* and counts the distinct values inside each fiber -- one
|
|
42
|
+
# count per fiber, not one count for the array. The two cannot be
|
|
43
|
+
# given together.
|
|
44
|
+
#
|
|
45
|
+
# Sub-arrays holding a masked cell do not participate and are not
|
|
46
|
+
# counted. `object` arrays are refused, because their cells hold
|
|
47
|
+
# Ruby references.
|
|
48
|
+
#
|
|
49
|
+
# @param along [Integer] axis whose index enumerates the sub-arrays.
|
|
50
|
+
# @return [Integer] the number of distinct sub-arrays.
|
|
51
|
+
def nunique (axis: nil, keep_axis: false, along: nil)
|
|
52
|
+
reject_axis_with_along(axis, along, "nunique")
|
|
53
|
+
return fibers_as_cells(along, "nunique").nunique if along
|
|
54
|
+
# Per-fiber single-pass seen-set hash (C __nunique__), one lane per data type
|
|
34
55
|
# family (numeric widen / NaN collapse, object rb_hash + rb_eql, fixlen
|
|
35
56
|
# byte-hash + memcmp). Masked cells are skipped; the accumulator is a no-op
|
|
36
57
|
# (the distinct count is the interned-key count).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
class CArray
|
|
2
|
+
|
|
3
|
+
# @overload repeat(count, axis: nil)
|
|
4
|
+
# @overload repeat(counts, axis: nil)
|
|
5
|
+
# Returns each element (or each sub-array) of `self` laid down
|
|
6
|
+
# `count` times in place, one after another.
|
|
7
|
+
#
|
|
8
|
+
# With an Integer, every element is repeated the same number of
|
|
9
|
+
# times. With an array of counts -- one per element, or one per
|
|
10
|
+
# sub-array when `axis:` is given -- each is repeated its own number
|
|
11
|
+
# of times, and a count of 0 drops that one:
|
|
12
|
+
#
|
|
13
|
+
# v = CA_INT([10, 20, 30])
|
|
14
|
+
# v.repeat(2) # => [ 10, 10, 20, 20, 30, 30 ]
|
|
15
|
+
# v.repeat([3, 1, 2]) # => [ 10, 10, 10, 20, 30, 30 ]
|
|
16
|
+
# v.repeat([2, 0, 1]) # => [ 10, 10, 30 ]
|
|
17
|
+
#
|
|
18
|
+
# This is `np.repeat`, and it is not {#tile}: `repeat` puts the
|
|
19
|
+
# copies of one element next to each other, `tile` lays the whole
|
|
20
|
+
# array down again -- `v.tile(2)` is `[10, 20, 30, 10, 20, 30]`.
|
|
21
|
+
#
|
|
22
|
+
# `axis: k` repeats the sub-arrays enumerated by axis `k` rather
|
|
23
|
+
# than the cells, so a 2-D array repeats whole rows and the result
|
|
24
|
+
# keeps its shape apart from that axis:
|
|
25
|
+
#
|
|
26
|
+
# t = CA_INT([[0, 1], [2, 3], [4, 5]])
|
|
27
|
+
# t.repeat([2, 1, 1], axis: 0)
|
|
28
|
+
# # => [ [ 0, 1 ],
|
|
29
|
+
# # [ 0, 1 ],
|
|
30
|
+
# # [ 2, 3 ],
|
|
31
|
+
# # [ 4, 5 ] ]
|
|
32
|
+
#
|
|
33
|
+
# Without `axis:` a multi-dimensional receiver is taken in flatten
|
|
34
|
+
# (row-major) order and the result is 1-D, as `np.repeat` does.
|
|
35
|
+
#
|
|
36
|
+
# The result is a **view** of `self` -- the same element named as
|
|
37
|
+
# many times as it was repeated -- so nothing is copied and writing
|
|
38
|
+
# through it reaches `self`, at every position that names the cell
|
|
39
|
+
# written. Take a `copy` when that is not wanted.
|
|
40
|
+
#
|
|
41
|
+
# Counts must be non-negative, and there must be exactly as many as
|
|
42
|
+
# there are elements (or sub-arrays along `axis:`). A masked count
|
|
43
|
+
# is refused: it names no number of repetitions.
|
|
44
|
+
#
|
|
45
|
+
# @param count [Integer, CArray, Array] how many times to repeat --
|
|
46
|
+
# one number for all, or one per element / sub-array.
|
|
47
|
+
# @param axis [Integer, nil] axis whose index enumerates the
|
|
48
|
+
# sub-arrays to repeat; `nil` repeats cells in flatten order.
|
|
49
|
+
# @return [CArray] the repeated elements, as a view of `self`.
|
|
50
|
+
def repeat (count, axis: nil)
|
|
51
|
+
along = axis.nil? ? nil : normalize_axis(axis, "repeat")
|
|
52
|
+
width = along ? shape[along] : elements
|
|
53
|
+
|
|
54
|
+
counts =
|
|
55
|
+
case count
|
|
56
|
+
when Integer
|
|
57
|
+
if count < 0
|
|
58
|
+
raise ArgumentError, "repeat: count must not be negative (got #{count})"
|
|
59
|
+
end
|
|
60
|
+
CArray.int64(width).fill(count)
|
|
61
|
+
else
|
|
62
|
+
given = count.to_ca.int64
|
|
63
|
+
unless given.elements == width
|
|
64
|
+
raise ArgumentError,
|
|
65
|
+
"repeat: #{given.elements} counts for #{width} " \
|
|
66
|
+
"#{along ? "sub-arrays along axis #{along}" : 'elements'}"
|
|
67
|
+
end
|
|
68
|
+
if given.has_mask? and given.count_masked > 0
|
|
69
|
+
raise ArgumentError,
|
|
70
|
+
"repeat: a masked count names no number of repetitions"
|
|
71
|
+
end
|
|
72
|
+
if given.lt(0).any
|
|
73
|
+
raise ArgumentError, "repeat: counts must not be negative"
|
|
74
|
+
end
|
|
75
|
+
given.flatten
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
source = along ? self : (ndim > 1 ? flatten : self)
|
|
79
|
+
source[*gather_args(repeat_index(counts), along)]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# The source index of every position in the result: element i appears
|
|
83
|
+
# at the `counts[i]` positions starting at the exclusive prefix sum.
|
|
84
|
+
#
|
|
85
|
+
# Marking those starts with +1 and running a cumulative sum turns the
|
|
86
|
+
# marks back into indices, and does the right thing with a zero
|
|
87
|
+
# count for free -- two elements then share a start, that position
|
|
88
|
+
# carries 2, and the sum steps over the one that was asked for zero
|
|
89
|
+
# times.
|
|
90
|
+
private def repeat_index (counts)
|
|
91
|
+
total = counts.sum.to_i
|
|
92
|
+
return CArray.int64(0) if total.zero?
|
|
93
|
+
starts = counts.cumsum.int64 - counts
|
|
94
|
+
marks = CArray.int64(total)
|
|
95
|
+
# A trailing zero count starts one past the end; it marks nothing,
|
|
96
|
+
# which is what a zero count means.
|
|
97
|
+
marks.scatter_add!(starts[starts.lt(total)], 1)
|
|
98
|
+
marks.cumsum.int64 - 1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# One index argument per axis: the gathered index on the axis being
|
|
102
|
+
# repeated, whole axes everywhere else.
|
|
103
|
+
private def gather_args (index, along)
|
|
104
|
+
return [index] unless along
|
|
105
|
+
args = [nil] * ndim
|
|
106
|
+
args[along] = index
|
|
107
|
+
args
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
data/lib/carray/methods/snap.rb
CHANGED
|
@@ -7,7 +7,7 @@ class CArray
|
|
|
7
7
|
# matching `CArray#round`; `:floor` snaps toward `-inf`, `:ceil`
|
|
8
8
|
# toward `+inf`).
|
|
9
9
|
#
|
|
10
|
-
# The output has the same
|
|
10
|
+
# The output has the same data type as `self` (integer input is coerced
|
|
11
11
|
# to float internally when `step` / `offset` are floats, following
|
|
12
12
|
# normal arithmetic promotion). NaN / Inf are preserved as a mask
|
|
13
13
|
# on the output; the rounding kernels map NaN to 0.0, which would
|
|
@@ -45,7 +45,12 @@ class CArray
|
|
|
45
45
|
# Detect NaN / Inf before rounding (which maps NaN -> 0.0 silently).
|
|
46
46
|
invalid_mask = scaled.float? ? scaled.is_invalid : nil
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
snapped = case direction
|
|
49
|
+
when :round then scaled.round
|
|
50
|
+
when :floor then scaled.floor
|
|
51
|
+
when :ceil then scaled.ceil
|
|
52
|
+
end
|
|
53
|
+
out = snapped * step + offset
|
|
49
54
|
|
|
50
55
|
if invalid_mask && invalid_mask.count(true) > 0
|
|
51
56
|
out.mask = out.has_mask? ? (out.mask | invalid_mask) : invalid_mask
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "carray/methods/discovery_along"
|
|
2
|
+
|
|
1
3
|
class CArray
|
|
2
4
|
|
|
3
5
|
# @overload unique(sort: false)
|
|
@@ -16,7 +18,7 @@ class CArray
|
|
|
16
18
|
# Masked cells do not participate and never appear in the result;
|
|
17
19
|
# an all-masked array yields an empty CArray.
|
|
18
20
|
#
|
|
19
|
-
# Distinctness follows `==` for numeric
|
|
21
|
+
# Distinctness follows `==` for numeric data types, with two float
|
|
20
22
|
# special cases so the result matches value-based expectations:
|
|
21
23
|
# all NaN collapse to a single distinct value (rather than one per
|
|
22
24
|
# cell) and -0.0 / +0.0 are the same value. The value kept for
|
|
@@ -32,10 +34,48 @@ class CArray
|
|
|
32
34
|
# its own unit: the distinct values are values, so the array comes back
|
|
33
35
|
# as itself rather than as raw storage ticks.
|
|
34
36
|
#
|
|
35
|
-
# @return [CArray] 1-D CArray of the distinct values, same
|
|
37
|
+
# @return [CArray] 1-D CArray of the distinct values, same data type
|
|
36
38
|
# as `self`.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
#
|
|
40
|
+
# @overload unique(along: k)
|
|
41
|
+
# Returns the distinct **sub-arrays** of `self`, comparing whole
|
|
42
|
+
# sub-arrays rather than cells: `along: k` names the axis whose
|
|
43
|
+
# index enumerates them, so `z.unique(along: 0)` gives the distinct
|
|
44
|
+
# rows of a 2-D array, in first-appearance order. This is
|
|
45
|
+
# `np.unique(z, axis=k)`.
|
|
46
|
+
#
|
|
47
|
+
# Note the contrast with `axis:` on {#nunique} and
|
|
48
|
+
# {#mask_duplicates}, which names the axis a *fiber runs along* and
|
|
49
|
+
# asks about the values inside each fiber. The two cannot be given
|
|
50
|
+
# together.
|
|
51
|
+
#
|
|
52
|
+
# Distinctness is the family's, widened from a cell to a sub-array:
|
|
53
|
+
# two sub-arrays are the same when every cell is, with all NaN one
|
|
54
|
+
# value and -0.0 == +0.0. A sub-array holding a masked cell does not
|
|
55
|
+
# participate and never appears in the result.
|
|
56
|
+
#
|
|
57
|
+
# The result is a **view** of `self` -- the surviving sub-arrays,
|
|
58
|
+
# not copies of them -- so writing through it reaches `self`. Take a
|
|
59
|
+
# `copy` when that is not wanted. `sort:` is not available here:
|
|
60
|
+
# sub-arrays have no order to sort by. `object` arrays are refused,
|
|
61
|
+
# because their cells hold Ruby references.
|
|
62
|
+
#
|
|
63
|
+
# @param along [Integer] axis whose index enumerates the sub-arrays.
|
|
64
|
+
# @return [CArray] the distinct sub-arrays, same shape as `self`
|
|
65
|
+
# except along `along`.
|
|
66
|
+
def unique (sort: false, along: nil)
|
|
67
|
+
if along
|
|
68
|
+
if sort
|
|
69
|
+
raise ArgumentError,
|
|
70
|
+
"unique: sort: is not available with along: -- sub-arrays have " \
|
|
71
|
+
"no order to sort by; the result is in first-appearance order"
|
|
72
|
+
end
|
|
73
|
+
keys = fibers_as_cells(along, "unique")
|
|
74
|
+
args = [nil] * ndim
|
|
75
|
+
args[normalize_axis(along, "unique")] = keys.mask_duplicates.is_not_masked
|
|
76
|
+
return self[*args]
|
|
77
|
+
end
|
|
78
|
+
# Single-pass seen-set hash (C __unique_flat__), one lane per data type family:
|
|
39
79
|
# integer widens to a 64-bit key; float uses the bitwise key with all-NaN
|
|
40
80
|
# collapsed and -0.0 / +0.0 normalized; object keys on rb_hash + rb_eql and
|
|
41
81
|
# fixlen on a byte-hash + memcmp, both reproducing Ruby Hash distinctness.
|
|
@@ -3,7 +3,7 @@ class CArray
|
|
|
3
3
|
# @overload value_counts(sort: false)
|
|
4
4
|
# Returns `[values, counts]`, the distinct values of `self` paired
|
|
5
5
|
# with the number of times each occurs. `values` is a 1-D CArray of
|
|
6
|
-
# `self`'s
|
|
6
|
+
# `self`'s data type; `counts` is a 1-D `CA_INT64` where `counts[i]` is
|
|
7
7
|
# the number of occurrences of `values[i]`. This is the frequency-
|
|
8
8
|
# table member of the value-hash discovery family ({#unique},
|
|
9
9
|
# {#mask_duplicates}, {#nunique}); like {#unique} it always
|
|
@@ -39,7 +39,7 @@ class CArray
|
|
|
39
39
|
raise ArgumentError, "value_counts: sort must be false, :count, or :value"
|
|
40
40
|
end
|
|
41
41
|
# Single-pass frequency-table hash (C __value_counts_flat__), one lane per
|
|
42
|
-
#
|
|
42
|
+
# data type family: integer widens to a 64-bit key; float uses the bitwise key
|
|
43
43
|
# with all NaN collapsed and -0.0 / +0.0 normalized; object keys on rb_hash +
|
|
44
44
|
# rb_eql and fixlen on a byte-hash + memcmp, both reproducing Ruby Hash
|
|
45
45
|
# distinctness. Masked cells are skipped in the kernel.
|
data/lib/carray/rng.rb
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
class CArray
|
|
2
|
+
|
|
3
|
+
# A random number generator with its own state.
|
|
4
|
+
#
|
|
5
|
+
# `Rng` rather than `Random`: a `CArray::Random` would shadow `::Random`
|
|
6
|
+
# for every bare `Random` written inside `class CArray`, and the two mean
|
|
7
|
+
# different generators -- `random!(rng: Random.new(4))` draws through
|
|
8
|
+
# Ruby's MT19937 and `random!(rng: CArray::Rng.new(seed: 4))` through the
|
|
9
|
+
# one below. Two names that different should not look alike.
|
|
10
|
+
#
|
|
11
|
+
# The class itself is defined in ext/carray_random.c, which is where the
|
|
12
|
+
# state is advanced. What is added here is the generator's source text,
|
|
13
|
+
# so that another gem can run the same code rather than a second
|
|
14
|
+
# implementation of it.
|
|
15
|
+
class Rng
|
|
16
|
+
|
|
17
|
+
# The generators this carray knows, and how many int64 cells each one
|
|
18
|
+
# keeps its state in.
|
|
19
|
+
STATE_CELLS = {
|
|
20
|
+
:xoshiro256pp => 4,
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
GENERATORS = STATE_CELLS.keys.freeze
|
|
24
|
+
|
|
25
|
+
# Where a generator's C lives. Shipped: the gemspec takes ext/*.h,
|
|
26
|
+
# and `ext` is a require path, so this resolves in a checkout and in
|
|
27
|
+
# an installed gem alike -- lib/ and ext/ are siblings in both.
|
|
28
|
+
SOURCE_FILES = {
|
|
29
|
+
:xoshiro256pp => File.expand_path("../../ext/ca_rng_xoshiro256pp.h",
|
|
30
|
+
__dir__),
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
# The C each generator is, as text.
|
|
34
|
+
#
|
|
35
|
+
# CArray::Rng::SOURCE[:xoshiro256pp] #=> "/* ---- ... */\n..."
|
|
36
|
+
#
|
|
37
|
+
# This is the same text the extension compiled -- the file below is
|
|
38
|
+
# `#include`d by ext/carray_random.c -- which is the point of handing
|
|
39
|
+
# it out. A caller that pastes it into a translation unit of its own
|
|
40
|
+
# gets a generator that continues a sequence this one started, because
|
|
41
|
+
# it is running the code this one ran and not a copy of it.
|
|
42
|
+
#
|
|
43
|
+
# carray-jit is the caller this exists for: a kernel's `random(rng:)`
|
|
44
|
+
# pastes the text beside the helpers its own compiler emits, so a
|
|
45
|
+
# kernel draws from the CArray::Rng it was handed. The text needs
|
|
46
|
+
# nothing but <stdint.h> and defines only `static inline` functions
|
|
47
|
+
# under a `ca_` prefix.
|
|
48
|
+
#
|
|
49
|
+
# It is read once, when this file is first required.
|
|
50
|
+
SOURCE = SOURCE_FILES.transform_values { |path|
|
|
51
|
+
File.read(path, :encoding => "UTF-8")
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
54
|
+
# What every generator's text needs beside it, and what none of them
|
|
55
|
+
# owns: turning uniforms into a normal is the same arithmetic whichever
|
|
56
|
+
# generator the uniforms came from.
|
|
57
|
+
#
|
|
58
|
+
# Handed out apart from SOURCE because whoever pastes it pastes it once,
|
|
59
|
+
# however many generators are drawing. Folded into each generator's own
|
|
60
|
+
# text instead, two generators in one translation unit would define it
|
|
61
|
+
# twice.
|
|
62
|
+
COMMON_SOURCE = File.read(
|
|
63
|
+
File.expand_path("../../ext/ca_rng_normal.h", __dir__), :encoding => "UTF-8"
|
|
64
|
+
).freeze
|
|
65
|
+
|
|
66
|
+
# The functions in SOURCE a caller may reach, by generator and by what
|
|
67
|
+
# the draw answers: `:random` a double in [0.0, 1.0), `:randomn` a
|
|
68
|
+
# standard normal, `:bits` the raw word a draw came from. Each takes the
|
|
69
|
+
# state and advances it -- `:randomn` by two draws, the other two by one.
|
|
70
|
+
#
|
|
71
|
+
# Named here rather than worked out by whoever pastes the text: which
|
|
72
|
+
# symbols are the entry points is a fact about the generator, and the
|
|
73
|
+
# generator is CArray's. These are the three `#random`, `#randomn`
|
|
74
|
+
# and `#bits` call, so a caller pasting the text draws the sequence
|
|
75
|
+
# this one draws.
|
|
76
|
+
DRAW_FUNCTIONS = {
|
|
77
|
+
:xoshiro256pp => {
|
|
78
|
+
:random => "ca_xoshiro256pp_next_real",
|
|
79
|
+
:randomn => "ca_xoshiro256pp_next_normal",
|
|
80
|
+
:bits => "ca_xoshiro256pp_next",
|
|
81
|
+
}.freeze,
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
data/lib/carray/runtime.rb
CHANGED
|
@@ -87,22 +87,3 @@ class CArray
|
|
|
87
87
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
class CAUnboundRepeat
|
|
91
|
-
# Overrides {CArray#template} for unbound-repeat views.
|
|
92
|
-
#
|
|
93
|
-
# The C-level `CArray#template` allocates an entity sized to `self.shape`,
|
|
94
|
-
# which for a CAUnboundRepeat would include the inflated unbound (`:*`)
|
|
95
|
-
# axes — semantically wrong (unbound axes are not yet bound to a size)
|
|
96
|
-
# and a memory bomb on broadcast patterns. Required for correctness.
|
|
97
|
-
#
|
|
98
|
-
# This override allocates at the **parent's compact shape** instead, then
|
|
99
|
-
# rewraps the result with the same `spec` so the returned view preserves
|
|
100
|
-
# the unbound broadcast pattern.
|
|
101
|
-
#
|
|
102
|
-
# @param argv [Array] forwarded to {CArray#template} (data_type, bytes:, ...)
|
|
103
|
-
# @yield forwarded to {CArray#template}
|
|
104
|
-
# @return [CAUnboundRepeat] new entity wrapped with the original unbound spec
|
|
105
|
-
def template (*argv, &block)
|
|
106
|
-
return parent.template(*argv,&block)[*spec.map{|x| x != :* ? nil : x}]
|
|
107
|
-
end
|
|
108
|
-
end
|
data/lib/carray/slab_iterator.rb
CHANGED
|
@@ -90,22 +90,76 @@ class CASlabIterator < CAIterator
|
|
|
90
90
|
# A per-slab reduction that folds each slab to one value is exactly the core
|
|
91
91
|
# per-axis reduction over the slab axes, so every reduction is a direct
|
|
92
92
|
# delegation to `reference.<op>(axis: slab_axes)`. This inherits the core
|
|
93
|
-
#
|
|
93
|
+
# data type, mask, empty/all-masked (identity vs UNDEF) and epsilon-close
|
|
94
94
|
# contracts unchanged -- there is no separate slab reduction kernel. (Unlike
|
|
95
95
|
# the map / reduce block surface, these are mask-aware: they route through the
|
|
96
96
|
# core reduction, which handles masked sources.)
|
|
97
97
|
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
|
|
98
|
+
# Each reduction below delegates to `reference.<op>(axis: slab_axes)`, so it
|
|
99
|
+
# inherits the core data type, mask, empty / all-masked and epsilon-close
|
|
100
|
+
# contracts unchanged. See {CAIterator} for what each one computes.
|
|
101
|
+
#
|
|
102
|
+
# @!method sum
|
|
103
|
+
# Per-slab sum.
|
|
104
|
+
# @return [CArray] one value per slab (shape = {#shape}).
|
|
105
|
+
# @!method accumulate
|
|
106
|
+
# Per-slab sum kept in the source's own data type, wrapping at its width,
|
|
107
|
+
# as the core `accumulate` does -- `sum` answers in the type the core
|
|
108
|
+
# promotes to (float64 for integers).
|
|
109
|
+
# @return [CArray] one value per slab
|
|
110
|
+
# @!method prod
|
|
111
|
+
# Per-slab product.
|
|
112
|
+
# @return [CArray] one value per slab
|
|
113
|
+
# @!method mean
|
|
114
|
+
# Per-slab arithmetic mean.
|
|
115
|
+
# @return [CArray] one value per slab
|
|
116
|
+
# @!method min
|
|
117
|
+
# Per-slab minimum.
|
|
118
|
+
# @return [CArray] one value per slab
|
|
119
|
+
# @!method max
|
|
120
|
+
# Per-slab maximum.
|
|
121
|
+
# @return [CArray] one value per slab
|
|
122
|
+
# @!method variance
|
|
123
|
+
# Per-slab sample variance (divisor `n - 1`).
|
|
124
|
+
# @return [CArray] one value per slab
|
|
125
|
+
# @!method stddev
|
|
126
|
+
# Per-slab sample standard deviation (divisor `n - 1`).
|
|
127
|
+
# @return [CArray] one value per slab
|
|
128
|
+
# @!method variancep
|
|
129
|
+
# Per-slab population variance (divisor `n`).
|
|
130
|
+
# @return [CArray] one value per slab
|
|
131
|
+
# @!method stddevp
|
|
132
|
+
# Per-slab population standard deviation (divisor `n`).
|
|
133
|
+
# @return [CArray] one value per slab
|
|
134
|
+
# @!method all
|
|
135
|
+
# Whether every cell of each slab is true.
|
|
136
|
+
# @return [CArray] `:boolean`, one value per slab
|
|
137
|
+
# @!method any
|
|
138
|
+
# Whether any cell of each slab is true.
|
|
139
|
+
# @return [CArray] `:boolean`, one value per slab
|
|
140
|
+
# @!method minmax
|
|
141
|
+
# Per-slab minimum and maximum, found in one pass.
|
|
142
|
+
# @return [Array<CArray>] the pair `[min, max]`
|
|
143
|
+
# @!method min_index
|
|
144
|
+
# Per-slab position of the minimum, local to the slab axes.
|
|
145
|
+
# @return [CArray] one index per slab
|
|
146
|
+
# @!method max_index
|
|
147
|
+
# Per-slab position of the maximum, local to the slab axes.
|
|
148
|
+
# @return [CArray] one index per slab
|
|
149
|
+
# @!method min_addr
|
|
150
|
+
# Per-slab flat source address of the minimum -- which source cell holds it.
|
|
151
|
+
# @return [CArray] one flat address per slab
|
|
152
|
+
# @!method max_addr
|
|
153
|
+
# Per-slab flat source address of the maximum.
|
|
154
|
+
# @return [CArray] one flat address per slab
|
|
155
|
+
[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
|
|
106
156
|
:variancep, :stddevp, :minmax, :min_index, :max_index,
|
|
107
157
|
:min_addr, :max_addr].each do |op|
|
|
108
|
-
|
|
158
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
159
|
+
def #{op}
|
|
160
|
+
@reference.#{op}(axis: @slab_axes)
|
|
161
|
+
end
|
|
162
|
+
RUBY
|
|
109
163
|
end
|
|
110
164
|
|
|
111
165
|
# @overload count(v = <none>)
|
|
@@ -243,7 +297,7 @@ class CASlabIterator < CAIterator
|
|
|
243
297
|
#
|
|
244
298
|
# A per-slab running accumulation along the slab axis is exactly the core
|
|
245
299
|
# per-axis cumulative over that axis, so every scan delegates to
|
|
246
|
-
# `reference.<op>(axis: slab_axis)`, inheriting the core
|
|
300
|
+
# `reference.<op>(axis: slab_axis)`, inheriting the core data type / mask (masked
|
|
247
301
|
# cells hold the running total, output unmasked) contracts unchanged. Each
|
|
248
302
|
# cell is in exactly one slab (a partition), so the running value is
|
|
249
303
|
# well-defined; the surface is uniform with the family even though it
|
|
@@ -251,23 +305,27 @@ class CASlabIterator < CAIterator
|
|
|
251
305
|
# multi-axis running accumulation is ambiguous), so a multi-axis slab raises --
|
|
252
306
|
# scan one axis at a time, like the order surface.
|
|
253
307
|
|
|
254
|
-
#
|
|
308
|
+
# @!method cumsum
|
|
255
309
|
# Per-slab inclusive running sum (float64), reference-shaped.
|
|
256
310
|
# @return [CArray]
|
|
257
|
-
#
|
|
311
|
+
# @!method cumprod
|
|
258
312
|
# Per-slab inclusive running product (float64), reference-shaped.
|
|
259
313
|
# @return [CArray]
|
|
260
|
-
#
|
|
261
|
-
# Per-slab inclusive running maximum (reference
|
|
314
|
+
# @!method cummax
|
|
315
|
+
# Per-slab inclusive running maximum (reference data type), reference-shaped.
|
|
262
316
|
# @return [CArray]
|
|
263
|
-
#
|
|
264
|
-
# Per-slab inclusive running minimum (reference
|
|
317
|
+
# @!method cummin
|
|
318
|
+
# Per-slab inclusive running minimum (reference data type), reference-shaped.
|
|
265
319
|
# @return [CArray]
|
|
266
|
-
#
|
|
320
|
+
# @!method cumcount
|
|
267
321
|
# Per-slab running count of present cells (int64), reference-shaped.
|
|
268
322
|
# @return [CArray]
|
|
269
323
|
[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
|
|
270
|
-
|
|
324
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
325
|
+
def #{op}
|
|
326
|
+
@reference.#{op}(axis: single_scan_axis(:#{op}))
|
|
327
|
+
end
|
|
328
|
+
RUBY
|
|
271
329
|
end
|
|
272
330
|
|
|
273
331
|
private
|
|
@@ -26,18 +26,18 @@ class CArray
|
|
|
26
26
|
# Boolean result -> :boolean CArray
|
|
27
27
|
module StringOperationMixin
|
|
28
28
|
|
|
29
|
-
# Per-cell map through the Face's scalar fetch. `
|
|
30
|
-
# CAString (string results); an explicit
|
|
29
|
+
# Per-cell map through the Face's scalar fetch. A nil `data_type` returns a
|
|
30
|
+
# CAString (string results); an explicit one returns a plain typed array
|
|
31
31
|
# (integer / boolean results). Shape and mask are carried.
|
|
32
|
-
def string_map (
|
|
33
|
-
out =
|
|
32
|
+
def string_map (data_type = nil) # :nodoc:
|
|
33
|
+
out = data_type ? template(data_type) : CArray.object(*shape)
|
|
34
34
|
if has_mask?
|
|
35
35
|
m = is_masked
|
|
36
36
|
elements.times { |i| out[i] = (m[i] ? UNDEF : yield(self[i])) }
|
|
37
37
|
else
|
|
38
38
|
elements.times { |i| out[i] = yield(self[i]) }
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
data_type ? out : CAString.wrap(out)
|
|
41
41
|
end
|
|
42
42
|
private :string_map
|
|
43
43
|
|
|
@@ -229,7 +229,11 @@ class CArray
|
|
|
229
229
|
else
|
|
230
230
|
values = Array.new(elements) { |i| self[i] }
|
|
231
231
|
end
|
|
232
|
-
CArray.const_string(values, encoding: encoding)
|
|
232
|
+
out = CArray.const_string(values, encoding: encoding)
|
|
233
|
+
# The builder takes a flat list, so give the shape back -- as
|
|
234
|
+
# to_fixlen_string above already does. Without this an N-D column
|
|
235
|
+
# came back 1-D.
|
|
236
|
+
ndim > 1 ? out.reshape(*shape) : out
|
|
233
237
|
end
|
|
234
238
|
|
|
235
239
|
# In-place transforms, mixed into the mutable Faces only (CAString /
|