carray 2.0.1 → 3.0.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.
- checksums.yaml +4 -4
- data/.yardopts +6 -25
- data/CHANGELOG.md +338 -0
- data/{NEWS.md → CHANGELOG.v1.md} +3 -0
- data/LICENSE +1 -1
- data/README.md +120 -36
- data/carray.gemspec +32 -30
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1244 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +124 -0
- data/ext/ca_binop_dispatch.h +152 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +239 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4367 -0
- data/ext/ca_kernel_iterator.h +2596 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +839 -0
- data/ext/ca_obj_binop.c +948 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +967 -0
- data/ext/ca_obj_face.c +750 -0
- data/ext/ca_obj_face.h +279 -0
- data/ext/ca_obj_fake.c +239 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +308 -0
- data/ext/ca_obj_grid.c +866 -440
- data/ext/ca_obj_meld.c +1039 -0
- data/ext/ca_obj_moncmp.c +588 -0
- data/ext/ca_obj_monop.c +1123 -0
- data/ext/ca_obj_object.c +866 -296
- data/ext/ca_obj_record.c +470 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +593 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +624 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1306 -0
- data/ext/ca_obj_shift.c +231 -793
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2584 -0
- data/ext/ca_obj_string.c +270 -0
- data/ext/ca_obj_tile.c +622 -0
- data/ext/ca_obj_time.c +548 -0
- data/ext/ca_obj_timedelta.c +437 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +785 -0
- data/ext/ca_obj_window.c +1202 -565
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +316 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +473 -0
- data/ext/ca_sweep_engine.h +166 -0
- data/ext/ca_transform_common.c +235 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +810 -420
- data/ext/carray_access.c +873 -731
- data/ext/carray_attribute.c +98 -329
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +376 -0
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2897 -874
- data/ext/carray_call_cfunc.h +313 -0
- data/ext/carray_cast.c +1264 -315
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +168 -270
- data/ext/carray_core.c +1396 -206
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1021 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +136 -0
- data/ext/carray_kernels_bincmp.c +4446 -0
- data/ext/carray_kernels_binop.c +11001 -0
- data/ext/carray_kernels_init.c +1131 -0
- data/ext/carray_kernels_map.c +3467 -0
- data/ext/carray_kernels_moncmp.c +2097 -0
- data/ext/carray_kernels_monop.c +18313 -0
- data/ext/carray_kernels_reduce_aggregate.c +25837 -0
- data/ext/carray_kernels_reduce_boolean.c +330 -0
- data/ext/carray_kernels_reduce_cumulative.c +14593 -0
- data/ext/carray_kernels_reduce_extreme.c +16948 -0
- data/ext/carray_kernels_reduce_variance.c +3910 -0
- data/ext/carray_kernels_scan.c +3693 -0
- data/ext/carray_kernels_search.c +32138 -0
- data/ext/carray_kernels_sort.c +10626 -0
- data/ext/carray_kernels_triop.c +1392 -0
- data/ext/carray_lazy.c +737 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +853 -158
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1650 -0
- data/ext/carray_operator.c +1525 -320
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +234 -55
- data/ext/mk_call_cfunc.rb +671 -0
- data/ext/mkkernel.rb +9096 -0
- data/ext/ruby_carray.c +211 -108
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +239 -0
- data/lib/carray/autoload_method_extension.rb +45 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +614 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1084 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +160 -328
- data/lib/carray/core_extensions.rb +297 -0
- data/lib/carray/data_type_extension.rb +250 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +642 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +321 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +316 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -26
- data/lib/carray/iterator.rb +58 -349
- data/lib/carray/lazy.rb +941 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +52 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +90 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +161 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +89 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +305 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2654 -38
- data/lib/carray/window_iterator.rb +927 -0
- data/lib/carray.rb +55 -57
- 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 +73 -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 +49 -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 +114 -0
- data/yard-stubs/carray_count.rb +79 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +23 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +132 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +89 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +312 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +57 -0
- data/yard-stubs/carray_sort.rb +163 -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 +220 -138
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/ca_obj_unbound_repeat.c +0 -529
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/mailmap +0 -1
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# CAFrame join (memo §5, §11.7, §13.3).
|
|
2
|
+
#
|
|
3
|
+
# Join is a thin delegation to landed addressing primitives: the join key
|
|
4
|
+
# yields an address array (locate_addr for a row-preserving left lookup,
|
|
5
|
+
# align_addr for symmetric set alignment), and each column is gathered by
|
|
6
|
+
# +project+ (length-preserving, miss -> UNDEF, Face-lift, read-only safe).
|
|
7
|
+
# The frame layer just distributes that address to every column.
|
|
8
|
+
|
|
9
|
+
class CAFrame
|
|
10
|
+
# Default disambiguation for non-key columns present on both sides (§12-C):
|
|
11
|
+
# left gets "_left", right gets "_right". +_left+/+_right+ read clearer than
|
|
12
|
+
# pandas' +_x+/+_y+ for the target audience.
|
|
13
|
+
DEFAULT_JOIN_SUFFIXES = ["_left", "_right"].freeze
|
|
14
|
+
private_constant :DEFAULT_JOIN_SUFFIXES
|
|
15
|
+
|
|
16
|
+
# Join +other+ on a shared key column.
|
|
17
|
+
#
|
|
18
|
+
# how: :left (default) — keep all left rows; right columns gathered per
|
|
19
|
+
# left row (locate_addr), misses become UNDEF.
|
|
20
|
+
# how: :inner/:outer/:right — set-align both key sets (align_addr) and
|
|
21
|
+
# gather both sides; the aligned key values form
|
|
22
|
+
# the +on+ column.
|
|
23
|
+
#
|
|
24
|
+
# A non-key column present on both sides collides. The key column (+on+) is
|
|
25
|
+
# kept once, never suffixed. By default the collision is resolved by suffixing
|
|
26
|
+
# both sides (+suffixes: ["_left", "_right"]+); pass a 2-element array to pick
|
|
27
|
+
# meaningful names up front (+["_obs", "_fcst"]+), or +suffixes: false+ to
|
|
28
|
+
# raise instead. Rename afterward with +rename+ if needed (§12-C).
|
|
29
|
+
def join(other, on:, how: :left, suffixes: DEFAULT_JOIN_SUFFIXES)
|
|
30
|
+
on = on.to_s
|
|
31
|
+
lkey = self[on]
|
|
32
|
+
rkey = other[on]
|
|
33
|
+
plan = join_name_plan(other, on, suffixes)
|
|
34
|
+
|
|
35
|
+
case how
|
|
36
|
+
when :left
|
|
37
|
+
join_left(other, on, lkey, rkey, plan)
|
|
38
|
+
when :inner, :outer, :right
|
|
39
|
+
join_align(other, on, lkey, rkey, how, plan)
|
|
40
|
+
else
|
|
41
|
+
raise ArgumentError, "unknown join mode #{how.inspect}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# As-of (nearest-key) left join for irregular series (memo §11.7). Each
|
|
46
|
+
# left row is matched to the nearest +other+ row by +on+ via
|
|
47
|
+
# locate_nearest_addr (the per-row, row-preserving counterpart of
|
|
48
|
+
# align_nearest_addr); +direction:+ follows CArray (:floor = most recent
|
|
49
|
+
# at-or-before, :ceil = next, :round = nearest), and rows with no match
|
|
50
|
+
# in range or beyond +tolerance:+ come back UNDEF. Same wiring as the left
|
|
51
|
+
# join, only the address primitive differs.
|
|
52
|
+
def join_asof(other, on:, direction: :floor, tolerance: nil, suffixes: DEFAULT_JOIN_SUFFIXES)
|
|
53
|
+
on = on.to_s
|
|
54
|
+
plan = join_name_plan(other, on, suffixes)
|
|
55
|
+
addr = self[on].locate_nearest_addr(other[on], direction: direction, tolerance: tolerance)
|
|
56
|
+
join_by_addr(other, on, addr, plan)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Conform every variable to an externally supplied reference key set (the
|
|
60
|
+
# asymmetric sibling of +join+; pandas +reindex+). Given a +reference+ array
|
|
61
|
+
# of key values, each column is gathered onto it by exact key match
|
|
62
|
+
# (+locate_addr+): the aligned key column (or index) becomes the reference
|
|
63
|
+
# itself, and a reference key absent from the source comes back UNDEF in
|
|
64
|
+
# every other column.
|
|
65
|
+
#
|
|
66
|
+
# CAFrame measures no interval and generates nothing -- the caller owns the
|
|
67
|
+
# reference. This is the primitive for reindexing to a caller-built axis
|
|
68
|
+
# (e.g. a complete 10-minute time grid): supply that axis as +reference+ and
|
|
69
|
+
# the gaps fill with UNDEF rows carrying only the reference key.
|
|
70
|
+
#
|
|
71
|
+
# df.align("time", reftime) # reftime = a CArray of reference key values
|
|
72
|
+
#
|
|
73
|
+
# +key+ may be a column name or the index axis name. +reference+ is a CArray
|
|
74
|
+
# (or Array); its keys are matched against the source key by value, so their
|
|
75
|
+
# data types must be comparable (a DateTime object key matches by eql?/hash).
|
|
76
|
+
def align(key, reference)
|
|
77
|
+
key = key.to_s
|
|
78
|
+
ref = reference.is_a?(CArray) ? reference : reference.to_ca
|
|
79
|
+
on_index = !@columns.key?(key) && @axis_name == key && @index
|
|
80
|
+
addr = ref.locate_addr(on_index ? @index : self[key])
|
|
81
|
+
|
|
82
|
+
cols = {}
|
|
83
|
+
variable_names.each { |name| cols[name] = project_rows(self[name], addr) }
|
|
84
|
+
if on_index
|
|
85
|
+
CAFrame.new(cols, axis_name: key, index: ref)
|
|
86
|
+
else
|
|
87
|
+
cols[key] = ref
|
|
88
|
+
CAFrame.new(cols, axis_name: @axis_name,
|
|
89
|
+
index: @index && project_rows(@index, addr))
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Paste +other+'s variables beside this frame's, matched by **row position**
|
|
94
|
+
# (memo §12-C) — a keyless column merge (the column-direction counterpart of
|
|
95
|
+
# the row-stacking +concat+, and the positional counterpart of the key-aligned
|
|
96
|
+
# +join+; named after the UNIX +paste+). Both frames must have the same
|
|
97
|
+
# +nrow+; rows are assumed to already correspond (no key alignment, consistent
|
|
98
|
+
# with the no-implicit-align stance). Colliding column names are disambiguated
|
|
99
|
+
# by the same policy as +join+ (default suffix +_left+/+_right+, +suffixes:+ to
|
|
100
|
+
# override, +suffixes: false+ to raise). This frame's index is kept; +other+'s
|
|
101
|
+
# index, if any, is not carried (only its columns are pasted). Returns a new
|
|
102
|
+
# frame.
|
|
103
|
+
#
|
|
104
|
+
# obs.paste(fcst) # side by side, same rows
|
|
105
|
+
# obs.paste(fcst, suffixes: ["_obs", "_fcst"])
|
|
106
|
+
def paste(other, suffixes: DEFAULT_JOIN_SUFFIXES)
|
|
107
|
+
CAFrame.paste(self, other, suffixes: suffixes)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Paste N frames side by side by row position (symmetric N-ary sibling of
|
|
111
|
+
# the instance +#paste+; see the instance verb's docstring for the pair
|
|
112
|
+
# semantics). All frames must have the same +nrow+. The result's
|
|
113
|
+
# +axis_name+ and +index+ come from the first frame (peers, but a base is
|
|
114
|
+
# needed for the index). Column names that appear in more than one frame
|
|
115
|
+
# are colliding — pass +suffixes:+ as an Array of exactly K strings (one
|
|
116
|
+
# per input frame) to disambiguate them; otherwise the collision raises.
|
|
117
|
+
#
|
|
118
|
+
# CAFrame.paste(obs, fcst)
|
|
119
|
+
# CAFrame.paste(obs, ecmwf, gfs, suffixes: ["_obs", "_ecmwf", "_gfs"])
|
|
120
|
+
# CAFrame.paste([obs, fcst]) # Array is accepted too
|
|
121
|
+
def self.paste(*frames, suffixes: DEFAULT_JOIN_SUFFIXES)
|
|
122
|
+
frames = frames.flatten
|
|
123
|
+
raise ArgumentError, "paste requires at least one frame" if frames.empty?
|
|
124
|
+
unless frames.all? { |f| f.is_a?(CAFrame) }
|
|
125
|
+
raise ArgumentError, "paste expects CAFrame arguments"
|
|
126
|
+
end
|
|
127
|
+
first = frames.first
|
|
128
|
+
nrow = first.nrow
|
|
129
|
+
frames.each_with_index do |f, i|
|
|
130
|
+
next if i.zero?
|
|
131
|
+
unless f.nrow == nrow
|
|
132
|
+
raise ArgumentError,
|
|
133
|
+
"paste: row-count mismatch (frame #{i}: #{f.nrow} vs #{nrow}); " \
|
|
134
|
+
"use join for key alignment"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Detect columns that appear in 2+ frames (collisions across the K inputs).
|
|
139
|
+
name_count = Hash.new(0)
|
|
140
|
+
frames.each { |f| f.variable_names.each { |name| name_count[name] += 1 } }
|
|
141
|
+
collisions = name_count.select { |_, n| n > 1 }.keys
|
|
142
|
+
|
|
143
|
+
if collisions.any?
|
|
144
|
+
if suffixes == false
|
|
145
|
+
raise ArgumentError,
|
|
146
|
+
"paste column name collision: #{collisions.inspect} " \
|
|
147
|
+
"(pass suffixes: [\"_a\", \"_b\", ...] with one entry per frame, " \
|
|
148
|
+
"or rename first)"
|
|
149
|
+
end
|
|
150
|
+
# For the 2-frame case DEFAULT_JOIN_SUFFIXES ("_left", "_right") works
|
|
151
|
+
# as-is; for K > 2 the caller must pass a K-length Array (or use the
|
|
152
|
+
# pair-only default indirectly via the instance method).
|
|
153
|
+
unless suffixes.is_a?(Array) && suffixes.size == frames.size &&
|
|
154
|
+
suffixes.all? { |s| s.is_a?(String) }
|
|
155
|
+
raise ArgumentError,
|
|
156
|
+
"paste: suffixes must be an Array of #{frames.size} Strings " \
|
|
157
|
+
"(one per frame), got #{suffixes.inspect}"
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
cols = {}
|
|
162
|
+
frames.each_with_index do |f, i|
|
|
163
|
+
f.variable_names.each do |name|
|
|
164
|
+
key = collisions.include?(name) ? "#{name}#{suffixes[i]}" : name
|
|
165
|
+
if cols.key?(key)
|
|
166
|
+
raise ArgumentError,
|
|
167
|
+
"paste: column name #{key.inspect} produced twice " \
|
|
168
|
+
"(collision after suffix from frame #{i})"
|
|
169
|
+
end
|
|
170
|
+
cols[key] = f[name]
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
new(cols, axis_name: first.axis_name, index: first.index)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private def join_name_plan(other, on, suffixes)
|
|
177
|
+
collisions = (variable_names & other.variable_names) - [on]
|
|
178
|
+
return { collisions: [], lsuf: nil, rsuf: nil } if collisions.empty?
|
|
179
|
+
|
|
180
|
+
if suffixes == false
|
|
181
|
+
raise ArgumentError,
|
|
182
|
+
"join column name collision: #{collisions.inspect} " \
|
|
183
|
+
"(pass suffixes: [\"_l\", \"_r\"] to disambiguate, or rename first)"
|
|
184
|
+
end
|
|
185
|
+
unless suffixes.is_a?(Array) && suffixes.size == 2 && suffixes.all? { |s| s.is_a?(String) }
|
|
186
|
+
raise ArgumentError,
|
|
187
|
+
"suffixes must be false or a 2-element array of Strings, got #{suffixes.inspect}"
|
|
188
|
+
end
|
|
189
|
+
{ collisions: collisions, lsuf: suffixes[0], rsuf: suffixes[1] }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private def join_name(name, suffix, plan)
|
|
193
|
+
plan[:collisions].include?(name) ? "#{name}#{suffix}" : name
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private def put_join_column(cols, name, col)
|
|
197
|
+
if cols.key?(name)
|
|
198
|
+
raise ArgumentError,
|
|
199
|
+
"join produces a duplicate column #{name.inspect} after suffixing; " \
|
|
200
|
+
"rename a column first or pass different suffixes"
|
|
201
|
+
end
|
|
202
|
+
cols[name] = col
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
private def join_left(other, on, lkey, rkey, plan)
|
|
206
|
+
join_by_addr(other, on, lkey.locate_addr(rkey), plan)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private def join_by_addr(other, on, addr, plan)
|
|
210
|
+
cols = {}
|
|
211
|
+
variable_names.each { |name| put_join_column(cols, join_name(name, plan[:lsuf], plan), self[name]) }
|
|
212
|
+
other.variable_names.each do |name|
|
|
213
|
+
next if name == on
|
|
214
|
+
put_join_column(cols, join_name(name, plan[:rsuf], plan), project_rows(other[name], addr))
|
|
215
|
+
end
|
|
216
|
+
CAFrame.new(cols, axis_name: @axis_name, index: @index)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
private def join_align(other, on, lkey, rkey, how, plan)
|
|
220
|
+
common, a_idx, b_idx = CArray.align_addr(lkey, rkey, join: how)
|
|
221
|
+
cols = {}
|
|
222
|
+
variable_names.each do |name|
|
|
223
|
+
if name == on
|
|
224
|
+
put_join_column(cols, on, common)
|
|
225
|
+
else
|
|
226
|
+
put_join_column(cols, join_name(name, plan[:lsuf], plan), project_rows(self[name], a_idx))
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
other.variable_names.each do |name|
|
|
230
|
+
next if name == on
|
|
231
|
+
put_join_column(cols, join_name(name, plan[:rsuf], plan), project_rows(other[name], b_idx))
|
|
232
|
+
end
|
|
233
|
+
# Carry the left index the same way its columns are gathered (by a_idx),
|
|
234
|
+
# so it stays consistent with the left join; rows with no left match
|
|
235
|
+
# (outer/right) become UNDEF.
|
|
236
|
+
new_index = @index && project_rows(@index, a_idx)
|
|
237
|
+
CAFrame.new(cols, axis_name: @axis_name, index: new_index)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
private def project_rows(col, addr)
|
|
241
|
+
return col.project(addr) if col.ndim == 1
|
|
242
|
+
trailing = col.shape[1..]
|
|
243
|
+
t = trailing.inject(1, :*)
|
|
244
|
+
n = addr.shape[0]
|
|
245
|
+
flat = addr.reshape(n, 1) * t + CArray.int64(t).seq.reshape(1, t)
|
|
246
|
+
col.project(flat).reshape(n, *trailing)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# CAFrame record (row-oriented) input (memo §11.2, §11.5).
|
|
2
|
+
#
|
|
3
|
+
# from_records takes an Array of row Hashes -- the shape JSON.parse yields for
|
|
4
|
+
# a JSON array of objects -- and arranges it into columns. Unlike from_csv,
|
|
5
|
+
# the cell values are already typed Ruby objects (Float / Integer / DateTime /
|
|
6
|
+
# String), so a homogeneous column is built at its native leaf type rather than
|
|
7
|
+
# left as strings ("arrange by the value's own type", not string inference --
|
|
8
|
+
# §4.2 stays intact: no date-like string is parsed, mixed columns stay object).
|
|
9
|
+
#
|
|
10
|
+
# An Array-valued cell that is the same length across every record becomes an
|
|
11
|
+
# N-D column (§11.5): { "temp" => [min, mean, max] } over N records is one
|
|
12
|
+
# (N, 3) column. Ragged or non-numeric arrays fall back to an object column.
|
|
13
|
+
#
|
|
14
|
+
# Missing keys and explicit nils become UNDEF for numeric columns (mask, not a
|
|
15
|
+
# float promotion): an int column with a hole stays int + UNDEF.
|
|
16
|
+
|
|
17
|
+
class CAFrame
|
|
18
|
+
# Build a frame from an Array of row Hashes. Column set is the union of keys
|
|
19
|
+
# in first-appearance order; keys are stringified. +types:+ casts named
|
|
20
|
+
# columns afterward (same map / array-key forms as +cast+).
|
|
21
|
+
def self.from_records(records, types: nil)
|
|
22
|
+
unless records.is_a?(Array) && records.all? { |r| r.is_a?(Hash) }
|
|
23
|
+
raise ArgumentError, "from_records expects an Array of Hashes"
|
|
24
|
+
end
|
|
25
|
+
return new if records.empty?
|
|
26
|
+
|
|
27
|
+
keys = record_key_union(records)
|
|
28
|
+
n = records.size
|
|
29
|
+
cols = {}
|
|
30
|
+
keys.each do |key|
|
|
31
|
+
values = records.map { |r| r[key] }
|
|
32
|
+
cols[key.to_s] = build_record_column(values, n)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
frame = new(cols)
|
|
36
|
+
frame.cast(types) if types
|
|
37
|
+
frame
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Union of record keys in first-appearance order (original key objects, so a
|
|
41
|
+
# String- or Symbol-keyed set both work; the column name stringifies later).
|
|
42
|
+
def self.record_key_union(records)
|
|
43
|
+
seen = {}
|
|
44
|
+
keys = []
|
|
45
|
+
records.each do |r|
|
|
46
|
+
r.each_key do |k|
|
|
47
|
+
unless seen.key?(k)
|
|
48
|
+
seen[k] = true
|
|
49
|
+
keys << k
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
keys
|
|
54
|
+
end
|
|
55
|
+
private_class_method :record_key_union
|
|
56
|
+
|
|
57
|
+
# Arrange one column's values (Ruby objects, nil for missing) into a CArray.
|
|
58
|
+
# All-Array cells of equal length -> N-D native column; numeric scalars ->
|
|
59
|
+
# native scalar column (nil -> UNDEF); anything else -> object column.
|
|
60
|
+
def self.build_record_column(values, n)
|
|
61
|
+
present = values.reject(&:nil?)
|
|
62
|
+
return CArray.object(n) { values } if present.empty?
|
|
63
|
+
|
|
64
|
+
if present.all? { |v| v.is_a?(Array) || v.is_a?(CArray) }
|
|
65
|
+
build_nd_column(values, present, n)
|
|
66
|
+
else
|
|
67
|
+
type = numeric_leaf_type(present)
|
|
68
|
+
col = CArray.object(n) { values }
|
|
69
|
+
type ? col.to_type(type) : col
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
private_class_method :build_record_column
|
|
73
|
+
|
|
74
|
+
# Stack equal-length array cells into an (N, L) column via an object 2-D fill
|
|
75
|
+
# + to_type (nil rows -> UNDEF, int/float by leaf). Ragged lengths or
|
|
76
|
+
# non-numeric leaves fall back to a 1-D object column of the raw cells.
|
|
77
|
+
def self.build_nd_column(values, present, n)
|
|
78
|
+
lengths = present.map { |v| v.is_a?(CArray) ? v.shape[0] : v.size }
|
|
79
|
+
len = lengths.first
|
|
80
|
+
return CArray.object(n) { values } unless lengths.all? { |x| x == len }
|
|
81
|
+
|
|
82
|
+
nested = values.map { |v| v.nil? ? Array.new(len) : (v.is_a?(CArray) ? v.to_a : v) }
|
|
83
|
+
type = numeric_leaf_type(nested.flatten.compact)
|
|
84
|
+
table = CArray.object(n, len) { nested }
|
|
85
|
+
type ? table.to_type(type) : table
|
|
86
|
+
end
|
|
87
|
+
private_class_method :build_nd_column
|
|
88
|
+
|
|
89
|
+
# :int64 if every value is an Integer, :float64 if all are Numeric (int/float
|
|
90
|
+
# mix), otherwise nil (strings / DateTime / booleans / mixed -> keep object).
|
|
91
|
+
def self.numeric_leaf_type(values)
|
|
92
|
+
if values.all? { |v| v.is_a?(Integer) }
|
|
93
|
+
:int64
|
|
94
|
+
elsif values.all? { |v| v.is_a?(Numeric) }
|
|
95
|
+
:float64
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
private_class_method :numeric_leaf_type
|
|
99
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# CAFrame row sort (memo §5, spine = addressing + gather).
|
|
2
|
+
#
|
|
3
|
+
# Two entry points, both delegating to the multi-key CArray.sort_addr -- the
|
|
4
|
+
# class-method lexicographic sort that returns flat row addresses (distinct from
|
|
5
|
+
# the single-key instance CArray#sort_index) -- and gathering every column and
|
|
6
|
+
# the index by that address into a view-frame in key order:
|
|
7
|
+
#
|
|
8
|
+
# sort_by_key(*keys, order:, masked_position:) -- sort by named columns, with
|
|
9
|
+
# per-key or blanket ascending/descending.
|
|
10
|
+
# sort_by { |f| ... } -- sort by key CArrays the block builds (the escape,
|
|
11
|
+
# §4.3): derived or composite keys are ordinary column
|
|
12
|
+
# math.
|
|
13
|
+
#
|
|
14
|
+
# sort_addr is ascending, so a descending key is expressed by replacing it with
|
|
15
|
+
# its dense descending rank (CArray#order(descending: true, method: :dense)):
|
|
16
|
+
# this works for every data type (unlike negation, which cannot reverse a string and
|
|
17
|
+
# silently wraps an unsigned integer), and the *dense* rank keeps equal values
|
|
18
|
+
# on one rank so ties fall through to later keys in a multi-key sort.
|
|
19
|
+
# masked_position (CArray.sort_addr's kwarg) places masked key rows first or
|
|
20
|
+
# last (default last).
|
|
21
|
+
|
|
22
|
+
class CAFrame
|
|
23
|
+
# Sort rows by one or more key columns, lexicographic (the first key is
|
|
24
|
+
# primary), returning a view-frame. Delegates to +CArray.sort_addr+ for the
|
|
25
|
+
# row permutation and gathers every column and the index by it.
|
|
26
|
+
#
|
|
27
|
+
# Each key is a **column name** (or the index axis name), or a
|
|
28
|
+
# **`[name, :asc | :desc]`** pair for a per-key direction. The +order:+ keyword
|
|
29
|
+
# is the direction for bare-name keys (default +:asc+). +masked_position:+
|
|
30
|
+
# sends masked key rows to the +:last+ (default) or +:first+ end.
|
|
31
|
+
#
|
|
32
|
+
# df.sort_by_key("temp") # one column, ascending
|
|
33
|
+
# df.sort_by_key("station", "temp") # lexicographic
|
|
34
|
+
# df.sort_by_key("temp", order: :desc) # all keys descending
|
|
35
|
+
# df.sort_by_key(["station", :asc], ["temp", :desc]) # per-key direction
|
|
36
|
+
# df.sort_by_key("temp", masked_position: :first) # masked rows first
|
|
37
|
+
# df.sort_by_key("time") # by the index
|
|
38
|
+
def sort_by_key(*specs, order: :asc, masked_position: :last)
|
|
39
|
+
raise ArgumentError, "sort_by_key requires at least one key" if specs.empty?
|
|
40
|
+
validate_sort_order(order)
|
|
41
|
+
keys = specs.map do |spec|
|
|
42
|
+
name, dir = parse_sort_key_spec(spec, order)
|
|
43
|
+
col = sort_key_column(name)
|
|
44
|
+
dir == :desc ? col.order(descending: true, method: :dense) : col
|
|
45
|
+
end
|
|
46
|
+
select_rows(CArray.sort_addr(*keys, masked_position: masked_position))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Sort rows by key columns the block builds (memo §4.3 escape). The block
|
|
50
|
+
# receives the frame and returns a key CArray, or an Array of them for a
|
|
51
|
+
# multi-key lexicographic sort; rows are sorted ascending by +CArray.sort_addr+
|
|
52
|
+
# over those keys and gathered into a view-frame. This is the sort sibling of
|
|
53
|
+
# +filter+: where +sort_by_key+ takes plain column names, the block form lets
|
|
54
|
+
# you build any key with CArray ops -- a descending key is its dense
|
|
55
|
+
# descending rank, derived or composite keys are ordinary column math.
|
|
56
|
+
# +masked_position:+ places masked key rows first or last (default last).
|
|
57
|
+
#
|
|
58
|
+
# df.sort_by { |f| (f["temp"] - target).abs } # nearest-to-target first
|
|
59
|
+
# df.sort_by { |f| f["temp"].order(descending: true, method: :dense) }
|
|
60
|
+
def sort_by(masked_position: :last)
|
|
61
|
+
raise ArgumentError, "sort_by requires a block" unless block_given?
|
|
62
|
+
keys = yield(self)
|
|
63
|
+
keys = [keys] unless keys.is_a?(Array)
|
|
64
|
+
raise ArgumentError, "sort_by block must return a key CArray or an Array of them" if keys.empty?
|
|
65
|
+
keys.each_with_index do |k, i|
|
|
66
|
+
unless k.is_a?(CArray)
|
|
67
|
+
raise ArgumentError, "sort_by key #{i} must be a CArray (got #{k.class})"
|
|
68
|
+
end
|
|
69
|
+
unless k.ndim == 1 && k.shape[0] == nrow
|
|
70
|
+
raise ArgumentError,
|
|
71
|
+
"sort_by key #{i} must be a 1-D column of length #{nrow} (got shape #{k.shape.inspect})"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
select_rows(CArray.sort_addr(*keys, masked_position: masked_position))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private def parse_sort_key_spec(spec, default_order)
|
|
78
|
+
case spec
|
|
79
|
+
when String, Symbol
|
|
80
|
+
[spec, default_order]
|
|
81
|
+
when Array
|
|
82
|
+
unless spec.size == 2 && (spec[0].is_a?(String) || spec[0].is_a?(Symbol))
|
|
83
|
+
raise ArgumentError, "sort key must be a name or [name, :asc|:desc], got #{spec.inspect}"
|
|
84
|
+
end
|
|
85
|
+
validate_sort_order(spec[1])
|
|
86
|
+
spec
|
|
87
|
+
else
|
|
88
|
+
raise ArgumentError, "sort key must be a name or [name, :asc|:desc], got #{spec.class}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private def validate_sort_order(dir)
|
|
93
|
+
return if dir == :asc || dir == :desc
|
|
94
|
+
raise ArgumentError, "sort order must be :asc or :desc, got #{dir.inspect}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private def sort_key_column(name)
|
|
98
|
+
key = name.to_s
|
|
99
|
+
col = if @columns.key?(key)
|
|
100
|
+
@columns[key]
|
|
101
|
+
elsif @index && @axis_name == key
|
|
102
|
+
@index
|
|
103
|
+
else
|
|
104
|
+
raise KeyError, "no column #{key.inspect}"
|
|
105
|
+
end
|
|
106
|
+
unless col.ndim == 1
|
|
107
|
+
raise ArgumentError,
|
|
108
|
+
"sort_by_key: key column #{key.inspect} is #{col.ndim}-D; " \
|
|
109
|
+
"sort by a scalar (1-D) column"
|
|
110
|
+
end
|
|
111
|
+
col
|
|
112
|
+
end
|
|
113
|
+
end
|