This is an automated email from the ASF dual-hosted git repository.

kou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new f12c18e  ARROW-13899: [Ruby] Implement slicer by compute kernels
f12c18e is described below

commit f12c18e619c168363e4edace8a09a591b87eee41
Author: Sutou Kouhei <[email protected]>
AuthorDate: Sun Sep 5 04:12:48 2021 +0900

    ARROW-13899: [Ruby] Implement slicer by compute kernels
    
    Closes #11083 from kou/ruby-slicer-expression
    
    Authored-by: Sutou Kouhei <[email protected]>
    Signed-off-by: Sutou Kouhei <[email protected]>
---
 c_glib/arrow-glib/compute.cpp          | 163 ++++++++++++++++++++++++++++
 c_glib/arrow-glib/compute.h            |  16 +++
 c_glib/arrow-glib/compute.hpp          |   4 +
 c_glib/test/test-is-in.rb              |  24 +++++
 c_glib/test/test-set-lookup-options.rb |  43 ++++++++
 ruby/red-arrow/lib/arrow/datum.rb      |   2 +
 ruby/red-arrow/lib/arrow/slicer.rb     | 187 ++++++++-------------------------
 ruby/red-arrow/test/test-slicer.rb     |  11 +-
 8 files changed, 301 insertions(+), 149 deletions(-)

diff --git a/c_glib/arrow-glib/compute.cpp b/c_glib/arrow-glib/compute.cpp
index 825d296..b083979 100644
--- a/c_glib/arrow-glib/compute.cpp
+++ b/c_glib/arrow-glib/compute.cpp
@@ -165,6 +165,9 @@ G_BEGIN_DECLS
  * #GArrowSortOptions is a class to customize the `sort_indices`
  * function.
  *
+ * #GArrowSetLookupOptions is a class to customize the `is_in` function
+ * and `index_in` function.
+ *
  * There are many functions to compute data on an array.
  */
 
@@ -2417,6 +2420,157 @@ garrow_sort_options_set_sort_keys(GArrowSortOptions 
*options,
 }
 
 
+typedef struct GArrowSetLookupOptionsPrivate_ {
+  GArrowDatum *value_set;
+} GArrowSetLookupOptionsPrivate;
+
+enum {
+  PROP_SET_LOOKUP_OPTIONS_VALUE_SET = 1,
+  PROP_SET_LOOKUP_OPTIONS_SKIP_NULLS,
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(GArrowSetLookupOptions,
+                           garrow_set_lookup_options,
+                           GARROW_TYPE_FUNCTION_OPTIONS)
+
+#define GARROW_SET_LOOKUP_OPTIONS_GET_PRIVATE(object) \
+  static_cast<GArrowSetLookupOptionsPrivate *>(       \
+    garrow_set_lookup_options_get_instance_private(   \
+      GARROW_SET_LOOKUP_OPTIONS(object)))
+
+static void
+garrow_set_lookup_options_dispose(GObject *object)
+{
+  auto priv = GARROW_SET_LOOKUP_OPTIONS_GET_PRIVATE(object);
+
+  if (priv->value_set) {
+    g_object_unref(priv->value_set);
+    priv->value_set = NULL;
+  }
+
+  G_OBJECT_CLASS(garrow_set_lookup_options_parent_class)->dispose(object);
+}
+
+static void
+garrow_set_lookup_options_set_property(GObject *object,
+                                       guint prop_id,
+                                       const GValue *value,
+                                       GParamSpec *pspec)
+{
+  auto priv = GARROW_SET_LOOKUP_OPTIONS_GET_PRIVATE(object);
+  auto options =
+    garrow_set_lookup_options_get_raw(GARROW_SET_LOOKUP_OPTIONS(object));
+
+  switch (prop_id) {
+  case PROP_SET_LOOKUP_OPTIONS_VALUE_SET:
+    priv->value_set = GARROW_DATUM(g_value_dup_object(value));
+    options->value_set = garrow_datum_get_raw(priv->value_set);
+    break;
+  case PROP_SET_LOOKUP_OPTIONS_SKIP_NULLS:
+    options->skip_nulls = g_value_get_boolean(value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+garrow_set_lookup_options_get_property(GObject *object,
+                                       guint prop_id,
+                                       GValue *value,
+                                       GParamSpec *pspec)
+{
+  auto priv = GARROW_SET_LOOKUP_OPTIONS_GET_PRIVATE(object);
+  auto options =
+    garrow_set_lookup_options_get_raw(GARROW_SET_LOOKUP_OPTIONS(object));
+
+  switch (prop_id) {
+  case PROP_SET_LOOKUP_OPTIONS_VALUE_SET:
+    g_value_set_object(value, priv->value_set);
+    break;
+  case PROP_SET_LOOKUP_OPTIONS_SKIP_NULLS:
+    g_value_set_boolean(value, options->skip_nulls);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+garrow_set_lookup_options_init(GArrowSetLookupOptions *object)
+{
+  auto priv = GARROW_FUNCTION_OPTIONS_GET_PRIVATE(object);
+  priv->options = static_cast<arrow::compute::FunctionOptions *>(
+    new arrow::compute::SetLookupOptions());
+}
+
+static void
+garrow_set_lookup_options_class_init(GArrowSetLookupOptionsClass *klass)
+{
+  auto gobject_class = G_OBJECT_CLASS(klass);
+
+  gobject_class->dispose = garrow_set_lookup_options_dispose;
+  gobject_class->set_property = garrow_set_lookup_options_set_property;
+  gobject_class->get_property = garrow_set_lookup_options_get_property;
+
+
+  arrow::compute::SetLookupOptions options;
+
+  GParamSpec *spec;
+  /**
+   * GArrowSetLookupOptions:value-set:
+   *
+   * The set of values to look up input values into.
+   *
+   * Since: 6.0.0
+   */
+  spec = g_param_spec_object("value-set",
+                             "Value set",
+                             "The set of values to look up input values into",
+                             GARROW_TYPE_DATUM,
+                             static_cast<GParamFlags>(G_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property(gobject_class,
+                                  PROP_SET_LOOKUP_OPTIONS_VALUE_SET,
+                                  spec);
+
+  /**
+   * GArrowSetLookupOptions:skip-nulls:
+   *
+   * Whether NULLs are skipped or not.
+   *
+   * Since: 6.0.0
+   */
+  spec = g_param_spec_boolean("skip-nulls",
+                              "Skip NULLs",
+                              "Whether NULLs are skipped or not",
+                              options.skip_nulls,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class,
+                                  PROP_SET_LOOKUP_OPTIONS_SKIP_NULLS,
+                                  spec);
+}
+
+/**
+ * garrow_set_lookup_options_new:
+ * @value_set: A #GArrowArrayDatum or #GArrowChunkedArrayDatum to be looked up.
+ *
+ * Returns: A newly created #GArrowSetLookupOptions.
+ *
+ * Since: 6.0.0
+ */
+GArrowSetLookupOptions *
+garrow_set_lookup_options_new(GArrowDatum *value_set)
+{
+  return GARROW_SET_LOOKUP_OPTIONS(
+    g_object_new(GARROW_TYPE_SET_LOOKUP_OPTIONS,
+                 "value-set", value_set,
+                 NULL));
+}
+
+
 /**
  * garrow_array_cast:
  * @array: A #GArrowArray.
@@ -3755,3 +3909,12 @@ garrow_sort_options_get_raw(GArrowSortOptions *options)
   return static_cast<arrow::compute::SortOptions *>(
     garrow_function_options_get_raw(GARROW_FUNCTION_OPTIONS(options)));
 }
+
+arrow::compute::SetLookupOptions *
+garrow_set_lookup_options_get_raw(GArrowSetLookupOptions *options)
+{
+  return static_cast<arrow::compute::SetLookupOptions *>(
+    garrow_function_options_get_raw(GARROW_FUNCTION_OPTIONS(options)));
+}
+
+
diff --git a/c_glib/arrow-glib/compute.h b/c_glib/arrow-glib/compute.h
index 108b27f..239cc50 100644
--- a/c_glib/arrow-glib/compute.h
+++ b/c_glib/arrow-glib/compute.h
@@ -436,6 +436,22 @@ garrow_sort_options_add_sort_key(GArrowSortOptions 
*options,
                                  GArrowSortKey *sort_key);
 
 
+#define GARROW_TYPE_SET_LOOKUP_OPTIONS (garrow_set_lookup_options_get_type())
+G_DECLARE_DERIVABLE_TYPE(GArrowSetLookupOptions,
+                         garrow_set_lookup_options,
+                         GARROW,
+                         SET_LOOKUP_OPTIONS,
+                         GArrowFunctionOptions)
+struct _GArrowSetLookupOptionsClass
+{
+  GArrowFunctionOptionsClass parent_class;
+};
+
+GARROW_AVAILABLE_IN_6_0
+GArrowSetLookupOptions *
+garrow_set_lookup_options_new(GArrowDatum *value_set);
+
+
 GArrowArray *garrow_array_cast(GArrowArray *array,
                                GArrowDataType *target_data_type,
                                GArrowCastOptions *options,
diff --git a/c_glib/arrow-glib/compute.hpp b/c_glib/arrow-glib/compute.hpp
index 01265ee..c616f6c 100644
--- a/c_glib/arrow-glib/compute.hpp
+++ b/c_glib/arrow-glib/compute.hpp
@@ -89,3 +89,7 @@ garrow_sort_key_get_raw(GArrowSortKey *sort_key);
 
 arrow::compute::SortOptions *
 garrow_sort_options_get_raw(GArrowSortOptions *options);
+
+
+arrow::compute::SetLookupOptions *
+garrow_set_lookup_options_get_raw(GArrowSetLookupOptions *options);
diff --git a/c_glib/test/test-is-in.rb b/c_glib/test/test-is-in.rb
index ba44075..590b5e3 100644
--- a/c_glib/test/test-is-in.rb
+++ b/c_glib/test/test-is-in.rb
@@ -46,6 +46,16 @@ class TestIsIn < Test::Unit::TestCase
       assert_equal(build_boolean_array([false, true, true, true]),
                    left.is_in(right))
     end
+
+    def test_options
+      left = build_int16_array([1, 0, nil, 2])
+      right = build_int16_array([2, 0, nil])
+      is_in = Arrow::Function.find("is_in")
+      options = Arrow::SetLookupOptions.new(Arrow::ArrayDatum.new(right))
+      assert_equal(build_boolean_array([false, true, true, true]),
+                   is_in.execute([Arrow::ArrayDatum.new(left)],
+                                 options).value)
+    end
   end
 
   sub_test_case("ChunkedArray") do
@@ -92,5 +102,19 @@ class TestIsIn < Test::Unit::TestCase
       assert_equal(build_boolean_array([false, true, true, true]),
                    left.is_in_chunked_array(right))
     end
+
+    def test_options
+      left = build_int16_array([1, 0, nil, 2])
+      chunks = [
+        build_int16_array([2, 0]),
+        build_int16_array([3, nil])
+      ]
+      right = Arrow::ChunkedArray.new(chunks)
+      is_in = Arrow::Function.find("is_in")
+      options = 
Arrow::SetLookupOptions.new(Arrow::ChunkedArrayDatum.new(right))
+      assert_equal(build_boolean_array([false, true, true, true]),
+                   is_in.execute([Arrow::ArrayDatum.new(left)],
+                                 options).value)
+    end
   end
 end
diff --git a/c_glib/test/test-set-lookup-options.rb 
b/c_glib/test/test-set-lookup-options.rb
new file mode 100644
index 0000000..779bace
--- /dev/null
+++ b/c_glib/test/test-set-lookup-options.rb
@@ -0,0 +1,43 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+class TestSetLookupOptions < Test::Unit::TestCase
+  include Helper::Buildable
+
+  def test_new
+    value_set = Arrow::ArrayDatum.new(build_int8_array([1, 2, 3]))
+    options = Arrow::SetLookupOptions.new(value_set)
+    assert_equal(value_set, options.value_set)
+  end
+
+  sub_test_case("instance methods") do
+    def setup
+      value_set = Arrow::ArrayDatum.new(build_int8_array([1, 2, 3]))
+      @options = Arrow::SetLookupOptions.new(value_set)
+    end
+
+    def test_skip_nulls
+      assert do
+        not @options.skip_nulls?
+      end
+      @options.skip_nulls = true
+      assert do
+        @options.skip_nulls?
+      end
+    end
+  end
+end
diff --git a/ruby/red-arrow/lib/arrow/datum.rb 
b/ruby/red-arrow/lib/arrow/datum.rb
index 99d1dae..196a18f 100644
--- a/ruby/red-arrow/lib/arrow/datum.rb
+++ b/ruby/red-arrow/lib/arrow/datum.rb
@@ -21,6 +21,8 @@ module Arrow
       # @api private
       def try_convert(value)
         case value
+        when Table
+          TableDatum.new(value)
         when Array
           ArrayDatum.new(value)
         when ChunkedArray
diff --git a/ruby/red-arrow/lib/arrow/slicer.rb 
b/ruby/red-arrow/lib/arrow/slicer.rb
index fa83476..6cca7f7 100644
--- a/ruby/red-arrow/lib/arrow/slicer.rb
+++ b/ruby/red-arrow/lib/arrow/slicer.rb
@@ -16,9 +16,6 @@
 # under the License.
 
 module Arrow
-  # Experimental
-  #
-  # TODO: Almost codes should be implemented in Apache Arrow C++.
   class Slicer
     def initialize(table)
       @table = table
@@ -43,6 +40,21 @@ module Arrow
       super
     end
 
+    module Helper
+      class << self
+        def ensure_boolean(column)
+          case column.data_type
+          when Arrow::BooleanDataType
+            column.data
+          else
+            options = CastOptions.new
+            options.to_data_type = Arrow::BooleanDataType.new
+            Function.find("cast").execute([column.data], options).value
+          end
+        end
+      end
+    end
+
     class Condition
       def evaluate
         message = "Slicer::Condition must define \#evaluate: #{inspect}"
@@ -69,43 +81,28 @@ module Arrow
       end
 
       def evaluate
-        values1 = @condition1.evaluate.each
-        values2 = @condition2.evaluate.each
-        raw_array = []
-        begin
-          loop do
-            value1 = values1.next
-            value2 = values2.next
-            if value1.nil? or value2.nil?
-              raw_array << nil
-            else
-              raw_array << evaluate_value(value1, value2)
-            end
-          end
-        rescue StopIteration
-        end
-        BooleanArray.new(raw_array)
+        function.execute([@condition1.evaluate, @condition2.evaluate]).value
       end
     end
 
     class AndCondition < LogicalCondition
       private
-      def evaluate_value(value1, value2)
-        value1 and value2
+      def function
+        Function.find("and")
       end
     end
 
     class OrCondition < LogicalCondition
       private
-      def evaluate_value(value1, value2)
-        value1 or value2
+      def function
+        Function.find("or")
       end
     end
 
     class XorCondition < LogicalCondition
       private
-      def evaluate_value(value1, value2)
-        value1 ^ value2
+      def function
+        Function.find("xor")
       end
     end
 
@@ -115,21 +112,7 @@ module Arrow
       end
 
       def evaluate
-        data = @column.data
-
-        case @column.data_type
-        when BooleanDataType
-          data
-        else
-          if data.n_chunks == 1
-            data.get_chunk(0).cast(BooleanDataType.new, nil)
-          else
-            arrays = data.each_chunk.collect do |chunk|
-              chunk.cast(BooleanDataType.new, nil)
-            end
-            ChunkedArray.new(arrays)
-          end
-        end
+        Helper.ensure_boolean(@column)
       end
 
       def !@
@@ -187,23 +170,8 @@ module Arrow
       end
 
       def evaluate
-        data = @column.data
-        raw_array = []
-        data.each_chunk do |chunk|
-          if chunk.is_a?(BooleanArray)
-            boolean_array = chunk
-          else
-            boolean_array = chunk.cast(BooleanDataType.new, nil)
-          end
-          boolean_array.each do |value|
-            if value.nil?
-              raw_array << value
-            else
-              raw_array << !value
-            end
-          end
-        end
-        BooleanArray.new(raw_array)
+        data = Helper.ensure_boolean(@column)
+        Function.find("invert").execute([data]).value
       end
 
       def !@
@@ -222,19 +190,10 @@ module Arrow
       end
 
       def evaluate
-        case @value
-        when nil
-          raw_array = @column.collect(&:nil?)
-          BooleanArray.new(raw_array)
+        if @value.nil?
+          Function.find("is_null").execute([@column.data]).value
         else
-          raw_array = @column.collect do |value|
-            if value.nil?
-              nil
-            else
-              @value == value
-            end
-          end
-          BooleanArray.new(raw_array)
+          Function.find("equal").execute([@column.data, @value]).value
         end
       end
     end
@@ -250,25 +209,10 @@ module Arrow
       end
 
       def evaluate
-        case @value
-        when nil
-          if @column.n_nulls.zero?
-            raw_array = [true] * @column.n_rows
-          else
-            raw_array = @column.n_rows.times.collect do |i|
-              @column.valid?(i)
-            end
-          end
-          BooleanArray.new(raw_array)
+        if @value.nil?
+          Function.find("is_valid").execute([@column.data]).value
         else
-          raw_array = @column.collect do |value|
-            if value.nil?
-              nil
-            else
-              @value != value
-            end
-          end
-          BooleanArray.new(raw_array)
+          Function.find("not_equal").execute([@column.data, @value]).value
         end
       end
     end
@@ -284,14 +228,7 @@ module Arrow
       end
 
       def evaluate
-        raw_array = @column.collect do |value|
-          if value.nil?
-            nil
-          else
-            @value > value
-          end
-        end
-        BooleanArray.new(raw_array)
+        Function.find("less").execute([@column.data, @value]).value
       end
     end
 
@@ -306,14 +243,7 @@ module Arrow
       end
 
       def evaluate
-        raw_array = @column.collect do |value|
-          if value.nil?
-            nil
-          else
-            @value >= value
-          end
-        end
-        BooleanArray.new(raw_array)
+        Function.find("less_equal").execute([@column.data, @value]).value
       end
     end
 
@@ -328,14 +258,7 @@ module Arrow
       end
 
       def evaluate
-        raw_array = @column.collect do |value|
-          if value.nil?
-            nil
-          else
-            @value < value
-          end
-        end
-        BooleanArray.new(raw_array)
+        Function.find("greater").execute([@column.data, @value]).value
       end
     end
 
@@ -350,14 +273,7 @@ module Arrow
       end
 
       def evaluate
-        raw_array = @column.collect do |value|
-          if value.nil?
-            nil
-          else
-            @value <= value
-          end
-        end
-        BooleanArray.new(raw_array)
+        Function.find("greater_equal").execute([@column.data, @value]).value
       end
     end
 
@@ -372,18 +288,10 @@ module Arrow
       end
 
       def evaluate
-        values_index = {}
-        @values.each do |value|
-          values_index[value] = true
-        end
-        raw_array = @column.collect do |value|
-          if value.nil?
-            nil
-          else
-            values_index.key?(value)
-          end
-        end
-        BooleanArray.new(raw_array)
+        values = @values
+        values = Array.new(values) unless values.is_a?(Array)
+        options = SetLookupOptions.new(values)
+        Function.find("is_in").execute([@column.data], options).value
       end
     end
 
@@ -398,18 +306,11 @@ module Arrow
       end
 
       def evaluate
-        values_index = {}
-        @values.each do |value|
-          values_index[value] = true
-        end
-        raw_array = @column.collect do |value|
-          if value.nil?
-            nil
-          else
-            not values_index.key?(value)
-          end
-        end
-        BooleanArray.new(raw_array)
+        values = @values
+        values = Array.new(values) unless values.is_a?(Array)
+        options = SetLookupOptions.new(values)
+        booleans = Function.find("is_in").execute([@column.data], 
options).value
+        Function.find("invert").execute([booleans]).value
       end
     end
 
diff --git a/ruby/red-arrow/test/test-slicer.rb 
b/ruby/red-arrow/test/test-slicer.rb
index b0f2dfa..4200866 100644
--- a/ruby/red-arrow/test/test-slicer.rb
+++ b/ruby/red-arrow/test/test-slicer.rb
@@ -349,12 +349,11 @@ class SlicerTest < Test::Unit::TestCase
       slicer.count.in?([1, 4, 16, 64])
     end
     assert_equal(<<-TABLE, sliced_table.to_s)
-        count  visible
-0           1  true   
-1           4   (null)
-2          16  true   
-3          64   (null)
-4      (null)   (null)
+       count   visible
+0          1   true   
+1          4    (null)
+2         16   true   
+3         64    (null)
     TABLE
   end
 

Reply via email to