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

lindong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-ml.git


The following commit(s) were added to refs/heads/master by this push:
     new fe2966d  [FLINK-29591] Add built-in UDFs to convert between arrays and 
vectors
fe2966d is described below

commit fe2966db6b5419459c98752cd1eaced9d4eb46ea
Author: yunfengzhou-hub <[email protected]>
AuthorDate: Tue Oct 18 10:55:26 2022 +0800

    [FLINK-29591] Add built-in UDFs to convert between arrays and vectors
    
    This closes #163.
---
 docs/content/docs/operators/functions.md           | 236 +++++++++++++++++++++
 .../flink/ml/examples/ArrayToVectorExample.java    |  58 +++++
 .../flink/ml/examples/VectorToArrayExample.java    |  64 ++++++
 .../main/java/org/apache/flink/ml/Functions.java   | 107 ++++++++++
 .../java/org/apache/flink/ml/FunctionsTest.java    | 132 ++++++++++++
 .../pyflink/examples/ml/arraytovector_example.py   |  60 ++++++
 .../pyflink/examples/ml/vectortoarray_example.py   |  62 ++++++
 flink-ml-python/pyflink/ml/core/wrapper.py         |  11 +-
 flink-ml-python/pyflink/ml/lib/functions.py        |  34 +++
 .../pyflink/ml/lib/tests/test_functions.py         | 114 ++++++++++
 10 files changed, 877 insertions(+), 1 deletion(-)

diff --git a/docs/content/docs/operators/functions.md 
b/docs/content/docs/operators/functions.md
new file mode 100644
index 0000000..0cddbab
--- /dev/null
+++ b/docs/content/docs/operators/functions.md
@@ -0,0 +1,236 @@
+---
+title: "Functions"
+type: docs
+weight: 2
+aliases:
+- /operators/functions.html
+---
+<!--
+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.
+-->
+
+## Functions
+
+Flink ML provides users with some built-in table functions for data
+transformations. This page gives a brief overview of them. 
+
+### vectorToArray
+
+This function converts a column of Flink ML sparse/dense vectors into a column
+of double arrays.
+
+{{< tabs vectorToArray_examples >}}
+
+{{< tab "Java">}}
+```java
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.linalg.typeinfo.VectorTypeInfo;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.CloseableIterator;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.flink.ml.Functions.vectorToArray;
+import static org.apache.flink.table.api.Expressions.$;
+
+/** Simple program that converts a column of dense/sparse vectors into a 
column of double arrays. */
+public class VectorToArrayExample {
+    public static void main(String[] args) {
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+        // Generates input vector data.
+        List<Vector> vectors =
+                Arrays.asList(
+                        Vectors.dense(0.0, 0.0),
+                        Vectors.sparse(2, new int[] {1}, new double[] {1.0}));
+        Table inputTable =
+                tEnv.fromDataStream(env.fromCollection(vectors, 
VectorTypeInfo.INSTANCE))
+                        .as("vector");
+
+        // Converts each vector to a double array.
+        Table outputTable = inputTable.select($("vector"), 
vectorToArray($("vector")).as("array"));
+
+        // Extracts and displays the results.
+        for (CloseableIterator<Row> it = outputTable.execute().collect(); 
it.hasNext(); ) {
+            Row row = it.next();
+            Vector vector = row.getFieldAs("vector");
+            Double[] doubleArray = row.getFieldAs("array");
+            System.out.printf(
+                    "Input vector: %s\tOutput double array: %s\n",
+                    vector, Arrays.toString(doubleArray));
+        }
+    }
+}
+```
+{{< /tab>}}
+
+{{< tab "Python">}}
+```python
+# Simple program that converts a column of dense/sparse vectors into a column 
of double arrays.
+
+from pyflink.common import Types
+from pyflink.datastream import StreamExecutionEnvironment
+from pyflink.table import StreamTableEnvironment
+
+from pyflink.ml.core.linalg import Vectors, VectorTypeInfo
+
+from pyflink.ml.lib.functions import vector_to_array
+from pyflink.table.expressions import col
+
+# create a new StreamExecutionEnvironment
+env = StreamExecutionEnvironment.get_execution_environment()
+
+# create a StreamTableEnvironment
+t_env = StreamTableEnvironment.create(env)
+
+# generate input vector data
+vectors = [
+    (Vectors.dense(0.0, 0.0),),
+    (Vectors.sparse(2, [1], [1.0]),),
+]
+input_table = t_env.from_data_stream(
+    env.from_collection(
+        vectors,
+        type_info=Types.ROW_NAMED(
+            ['vector'],
+            [VectorTypeInfo()])
+    ))
+
+# convert each vector to a double array
+output_table = 
input_table.select(vector_to_array(col('vector')).alias('array'))
+
+# extract and display the results
+output_values = [x for x in
+                 t_env.to_data_stream(output_table).map(lambda r: 
r).execute_and_collect()]
+
+output_values.sort(key=lambda x: x[0])
+
+field_names = output_table.get_schema().get_field_names()
+for i in range(len(output_values)):
+    vector = vectors[i][0]
+    double_array = output_values[i][field_names.index("array")]
+    print("Input vector: %s \t output double array: %s" % (vector, 
double_array))
+```
+{{< /tab>}}
+
+{{< /tabs>}}
+
+### arrayToVector
+
+This function converts a column of arrays of numeric type into a column of
+DenseVector instances.
+
+{{< tabs arrayToVector_examples >}}
+
+{{< tab "Java">}}
+```java
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.CloseableIterator;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.flink.ml.Functions.arrayToVector;
+import static org.apache.flink.table.api.Expressions.$;
+
+/** Simple program that converts a column of double arrays into a column of 
dense vectors. */
+public class ArrayToVectorExample {
+    public static void main(String[] args) {
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+        // Generates input double array data.
+        List<double[]> doubleArrays =
+                Arrays.asList(new double[] {0.0, 0.0}, new double[] {0.0, 
1.0});
+        Table inputTable = 
tEnv.fromDataStream(env.fromCollection(doubleArrays)).as("array");
+
+        // Converts each double array to a dense vector.
+        Table outputTable = inputTable.select($("array"), 
arrayToVector($("array")).as("vector"));
+
+        // Extracts and displays the results.
+        for (CloseableIterator<Row> it = outputTable.execute().collect(); 
it.hasNext(); ) {
+            Row row = it.next();
+            Double[] doubleArray = row.getFieldAs("array");
+            Vector vector = row.getFieldAs("vector");
+            System.out.printf(
+                    "Input double array: %s\tOutput vector: %s\n",
+                    Arrays.toString(doubleArray), vector);
+        }
+    }
+}
+```
+{{< /tab>}}
+
+{{< tab "Python">}}
+```python
+# Simple program that converts a column of double arrays into a column of 
dense vectors.
+
+from pyflink.common import Types
+from pyflink.datastream import StreamExecutionEnvironment
+from pyflink.ml.lib.functions import array_to_vector
+from pyflink.table import StreamTableEnvironment
+from pyflink.table.expressions import col
+
+# create a new StreamExecutionEnvironment
+env = StreamExecutionEnvironment.get_execution_environment()
+
+# create a StreamTableEnvironment
+t_env = StreamTableEnvironment.create(env)
+
+# generate input double array data
+double_arrays = [
+    ([0.0, 0.0],),
+    ([0.0, 1.0],),
+]
+input_table = t_env.from_data_stream(
+    env.from_collection(
+        double_arrays,
+        type_info=Types.ROW_NAMED(
+            ['array'],
+            [Types.PRIMITIVE_ARRAY(Types.DOUBLE())])
+    ))
+
+# convert each double array to a dense vector
+output_table = 
input_table.select(array_to_vector(col('array')).alias('vector'))
+
+# extract and display the results
+field_names = output_table.get_schema().get_field_names()
+
+output_values = [x[field_names.index('vector')] for x in
+                 t_env.to_data_stream(output_table).execute_and_collect()]
+
+output_values.sort(key=lambda x: x.get(1))
+
+for i in range(len(output_values)):
+    double_array = double_arrays[i][0]
+    vector = output_values[i]
+    print("Input double array: %s \t output vector: %s" % (double_array, 
vector))
+```
+{{< /tab>}}
+
+{{< /tabs>}}
diff --git 
a/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/ArrayToVectorExample.java
 
b/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/ArrayToVectorExample.java
new file mode 100644
index 0000000..85c57b7
--- /dev/null
+++ 
b/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/ArrayToVectorExample.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package org.apache.flink.ml.examples;
+
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.CloseableIterator;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.flink.ml.Functions.arrayToVector;
+import static org.apache.flink.table.api.Expressions.$;
+
+/** Simple program that converts a column of double arrays into a column of 
dense vectors. */
+public class ArrayToVectorExample {
+    public static void main(String[] args) {
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+        // Generates input double array data.
+        List<double[]> doubleArrays =
+                Arrays.asList(new double[] {0.0, 0.0}, new double[] {0.0, 
1.0});
+        Table inputTable = 
tEnv.fromDataStream(env.fromCollection(doubleArrays)).as("array");
+
+        // Converts each double array to a dense vector.
+        Table outputTable = inputTable.select($("array"), 
arrayToVector($("array")).as("vector"));
+
+        // Extracts and displays the results.
+        for (CloseableIterator<Row> it = outputTable.execute().collect(); 
it.hasNext(); ) {
+            Row row = it.next();
+            Double[] doubleArray = row.getFieldAs("array");
+            Vector vector = row.getFieldAs("vector");
+            System.out.printf(
+                    "Input double array: %s\tOutput vector: %s\n",
+                    Arrays.toString(doubleArray), vector);
+        }
+    }
+}
diff --git 
a/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/VectorToArrayExample.java
 
b/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/VectorToArrayExample.java
new file mode 100644
index 0000000..733fe45
--- /dev/null
+++ 
b/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/VectorToArrayExample.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package org.apache.flink.ml.examples;
+
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.linalg.typeinfo.VectorTypeInfo;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.CloseableIterator;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.flink.ml.Functions.vectorToArray;
+import static org.apache.flink.table.api.Expressions.$;
+
+/** Simple program that converts a column of dense/sparse vectors into a 
column of double arrays. */
+public class VectorToArrayExample {
+    public static void main(String[] args) {
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+        // Generates input vector data.
+        List<Vector> vectors =
+                Arrays.asList(
+                        Vectors.dense(0.0, 0.0),
+                        Vectors.sparse(2, new int[] {1}, new double[] {1.0}));
+        Table inputTable =
+                tEnv.fromDataStream(env.fromCollection(vectors, 
VectorTypeInfo.INSTANCE))
+                        .as("vector");
+
+        // Converts each vector to a double array.
+        Table outputTable = inputTable.select($("vector"), 
vectorToArray($("vector")).as("array"));
+
+        // Extracts and displays the results.
+        for (CloseableIterator<Row> it = outputTable.execute().collect(); 
it.hasNext(); ) {
+            Row row = it.next();
+            Vector vector = row.getFieldAs("vector");
+            Double[] doubleArray = row.getFieldAs("array");
+            System.out.printf(
+                    "Input vector: %s\tOutput double array: %s\n",
+                    vector, Arrays.toString(doubleArray));
+        }
+    }
+}
diff --git a/flink-ml-lib/src/main/java/org/apache/flink/ml/Functions.java 
b/flink-ml-lib/src/main/java/org/apache/flink/ml/Functions.java
new file mode 100644
index 0000000..694b35e
--- /dev/null
+++ b/flink-ml-lib/src/main/java/org/apache/flink/ml/Functions.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package org.apache.flink.ml;
+
+import org.apache.flink.ml.linalg.DenseVector;
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.linalg.typeinfo.DenseVectorTypeInfo;
+import org.apache.flink.table.api.ApiExpression;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.catalog.DataTypeFactory;
+import org.apache.flink.table.functions.ScalarFunction;
+import org.apache.flink.table.types.inference.TypeInference;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.Optional;
+
+import static org.apache.flink.table.api.Expressions.call;
+
+/** Built-in table functions for data transformations. */
+@SuppressWarnings("unused")
+public class Functions {
+    /** Converts a column of {@link Vector}s into a column of double arrays. */
+    public static ApiExpression vectorToArray(Object... arguments) {
+        return call(VectorToArrayFunction.class, arguments);
+    }
+
+    /**
+     * A {@link ScalarFunction} that converts a column of {@link Vector}s into 
a column of double
+     * arrays.
+     */
+    public static class VectorToArrayFunction extends ScalarFunction {
+        public double[] eval(Vector vector) {
+            return vector.toArray();
+        }
+
+        @Override
+        public TypeInference getTypeInference(DataTypeFactory typeFactory) {
+            return TypeInference.newBuilder()
+                    .outputTypeStrategy(
+                            callContext ->
+                                    Optional.of(
+                                            DataTypes.ARRAY(
+                                                    DataTypes.DOUBLE()
+                                                            .notNull()
+                                                            
.bridgedTo(double.class))))
+                    .build();
+        }
+    }
+
+    /**
+     * Converts a column of arrays of numeric type into a column of {@link 
DenseVector} instances.
+     */
+    public static ApiExpression arrayToVector(Object... arguments) {
+        return call(ArrayToVectorFunction.class, arguments);
+    }
+
+    /**
+     * A {@link ScalarFunction} that converts a column of arrays of numeric 
type into a column of
+     * {@link DenseVector} instances.
+     */
+    public static class ArrayToVectorFunction extends ScalarFunction {
+        public DenseVector eval(double[] array) {
+            return Vectors.dense(array);
+        }
+
+        public DenseVector eval(Double[] array) {
+            return eval(ArrayUtils.toPrimitive(array));
+        }
+
+        public DenseVector eval(Number[] array) {
+            double[] doubles = new double[array.length];
+            for (int i = 0; i < array.length; i++) {
+                doubles[i] = array[i].doubleValue();
+            }
+            return eval(doubles);
+        }
+
+        @Override
+        public TypeInference getTypeInference(DataTypeFactory typeFactory) {
+            return TypeInference.newBuilder()
+                    .outputTypeStrategy(
+                            callContext ->
+                                    Optional.of(
+                                            
DataTypes.of(DenseVectorTypeInfo.INSTANCE)
+                                                    .toDataType(typeFactory)))
+                    .build();
+        }
+    }
+}
diff --git a/flink-ml-lib/src/test/java/org/apache/flink/ml/FunctionsTest.java 
b/flink-ml-lib/src/test/java/org/apache/flink/ml/FunctionsTest.java
new file mode 100644
index 0000000..3346764
--- /dev/null
+++ b/flink-ml-lib/src/test/java/org/apache/flink/ml/FunctionsTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package org.apache.flink.ml;
+
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.ml.linalg.DenseVector;
+import org.apache.flink.ml.linalg.SparseVector;
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.linalg.typeinfo.VectorTypeInfo;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.test.util.AbstractTestBase;
+import org.apache.flink.types.Row;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.annotation.Nullable;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.flink.ml.Functions.arrayToVector;
+import static org.apache.flink.ml.Functions.vectorToArray;
+import static org.apache.flink.table.api.Expressions.$;
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+/** Tests {@link Functions}. */
+public class FunctionsTest extends AbstractTestBase {
+    private static final List<double[]> doubleArrays =
+            Arrays.asList(new double[] {0.0, 0.0}, new double[] {0.0, 1.0});
+
+    private static final List<float[]> floatArrays =
+            Arrays.asList(new float[] {0.0f, 0.0f}, new float[] {0.0f, 1.0f});
+
+    private static final List<int[]> intArrays = Arrays.asList(new int[] {0, 
0}, new int[] {0, 1});
+
+    private static final List<long[]> longArrays =
+            Arrays.asList(new long[] {0, 0}, new long[] {0, 1});
+
+    private static final List<DenseVector> denseVectors =
+            Arrays.asList(Vectors.dense(0.0, 0.0), Vectors.dense(0.0, 1.0));
+
+    private static final List<SparseVector> sparseVectors =
+            Arrays.asList(
+                    Vectors.sparse(2, new int[0], new double[0]),
+                    Vectors.sparse(2, new int[] {1}, new double[] {1.0}));
+
+    private static final List<Vector> mixedVectors =
+            Arrays.asList(
+                    Vectors.dense(0.0, 0.0), Vectors.sparse(2, new int[] {1}, 
new double[] {1.0}));
+
+    private StreamExecutionEnvironment env;
+    private StreamTableEnvironment tEnv;
+
+    @Before
+    public void before() {
+        env = StreamExecutionEnvironment.getExecutionEnvironment();
+        tEnv = StreamTableEnvironment.create(env);
+    }
+
+    @Test
+    public void testVectorToArray() {
+        testVectorToArray(denseVectors, null);
+        testVectorToArray(sparseVectors, null);
+        testVectorToArray(mixedVectors, VectorTypeInfo.INSTANCE);
+    }
+
+    private <T> void testVectorToArray(
+            List<T> vectors, @Nullable TypeInformation<T> 
vectorTypeInformation) {
+        Table inputTable;
+        if (vectorTypeInformation == null) {
+            inputTable = tEnv.fromDataStream(env.fromCollection(vectors));
+        } else {
+            inputTable = tEnv.fromDataStream(env.fromCollection(vectors, 
vectorTypeInformation));
+        }
+        inputTable = inputTable.as("vector");
+
+        Table outputTable = 
inputTable.select(vectorToArray($("vector")).as("array"));
+
+        List<Row> outputValues = 
IteratorUtils.toList(outputTable.execute().collect());
+
+        assertEquals(outputValues.size(), doubleArrays.size());
+        for (int i = 0; i < doubleArrays.size(); i++) {
+            Double[] doubles = outputValues.get(i).getFieldAs("array");
+            assertArrayEquals(doubleArrays.get(i), 
ArrayUtils.toPrimitive(doubles));
+        }
+    }
+
+    @Test
+    public void testArrayToVector() {
+        testArrayToVector(doubleArrays);
+        testArrayToVector(floatArrays);
+        testArrayToVector(intArrays);
+        testArrayToVector(longArrays);
+    }
+
+    private <T> void testArrayToVector(List<T> array) {
+        Table inputTable = 
tEnv.fromDataStream(env.fromCollection(array)).as("array");
+
+        Table outputTable = 
inputTable.select(arrayToVector($("array")).as("vector"));
+
+        List<Row> outputValues = 
IteratorUtils.toList(outputTable.execute().collect());
+
+        assertEquals(outputValues.size(), denseVectors.size());
+        for (int i = 0; i < denseVectors.size(); i++) {
+            DenseVector vector = outputValues.get(i).getFieldAs("vector");
+            assertEquals(denseVectors.get(i), vector);
+        }
+    }
+}
diff --git a/flink-ml-python/pyflink/examples/ml/arraytovector_example.py 
b/flink-ml-python/pyflink/examples/ml/arraytovector_example.py
new file mode 100644
index 0000000..78ad427
--- /dev/null
+++ b/flink-ml-python/pyflink/examples/ml/arraytovector_example.py
@@ -0,0 +1,60 @@
+################################################################################
+#  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.
+################################################################################
+
+# Simple program that converts a column of double arrays into a column of 
dense vectors.
+
+from pyflink.common import Types
+from pyflink.datastream import StreamExecutionEnvironment
+from pyflink.ml.lib.functions import array_to_vector
+from pyflink.table import StreamTableEnvironment
+from pyflink.table.expressions import col
+
+# create a new StreamExecutionEnvironment
+env = StreamExecutionEnvironment.get_execution_environment()
+
+# create a StreamTableEnvironment
+t_env = StreamTableEnvironment.create(env)
+
+# generate input double array data
+double_arrays = [
+    ([0.0, 0.0],),
+    ([0.0, 1.0],),
+]
+input_table = t_env.from_data_stream(
+    env.from_collection(
+        double_arrays,
+        type_info=Types.ROW_NAMED(
+            ['array'],
+            [Types.PRIMITIVE_ARRAY(Types.DOUBLE())])
+    ))
+
+# convert each double array to a dense vector
+output_table = 
input_table.select(array_to_vector(col('array')).alias('vector'))
+
+# extract and display the results
+field_names = output_table.get_schema().get_field_names()
+
+output_values = [x[field_names.index('vector')] for x in
+                 t_env.to_data_stream(output_table).execute_and_collect()]
+
+output_values.sort(key=lambda x: x.get(1))
+
+for i in range(len(output_values)):
+    double_array = double_arrays[i][0]
+    vector = output_values[i]
+    print("Input double array: %s \t output vector: %s" % (double_array, 
vector))
diff --git a/flink-ml-python/pyflink/examples/ml/vectortoarray_example.py 
b/flink-ml-python/pyflink/examples/ml/vectortoarray_example.py
new file mode 100644
index 0000000..0ead802
--- /dev/null
+++ b/flink-ml-python/pyflink/examples/ml/vectortoarray_example.py
@@ -0,0 +1,62 @@
+################################################################################
+#  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.
+################################################################################
+
+# Simple program that converts a column of dense/sparse vectors into a column 
of double arrays.
+
+from pyflink.common import Types
+from pyflink.datastream import StreamExecutionEnvironment
+from pyflink.table import StreamTableEnvironment
+
+from pyflink.ml.core.linalg import Vectors, VectorTypeInfo
+
+from pyflink.ml.lib.functions import vector_to_array
+from pyflink.table.expressions import col
+
+# create a new StreamExecutionEnvironment
+env = StreamExecutionEnvironment.get_execution_environment()
+
+# create a StreamTableEnvironment
+t_env = StreamTableEnvironment.create(env)
+
+# generate input vector data
+vectors = [
+    (Vectors.dense(0.0, 0.0),),
+    (Vectors.sparse(2, [1], [1.0]),),
+]
+input_table = t_env.from_data_stream(
+    env.from_collection(
+        vectors,
+        type_info=Types.ROW_NAMED(
+            ['vector'],
+            [VectorTypeInfo()])
+    ))
+
+# convert each vector to a double array
+output_table = 
input_table.select(vector_to_array(col('vector')).alias('array'))
+
+# extract and display the results
+output_values = [x for x in
+                 t_env.to_data_stream(output_table).map(lambda r: 
r).execute_and_collect()]
+
+output_values.sort(key=lambda x: x[0])
+
+field_names = output_table.get_schema().get_field_names()
+for i in range(len(output_values)):
+    vector = vectors[i][0]
+    double_array = output_values[i][field_names.index("array")]
+    print("Input vector: %s \t output double array: %s" % (vector, 
double_array))
diff --git a/flink-ml-python/pyflink/ml/core/wrapper.py 
b/flink-ml-python/pyflink/ml/core/wrapper.py
index 191d8de..e965c0c 100644
--- a/flink-ml-python/pyflink/ml/core/wrapper.py
+++ b/flink-ml-python/pyflink/ml/core/wrapper.py
@@ -22,7 +22,7 @@ from py4j.java_gateway import JavaObject, get_java_class
 from pyflink.common import typeinfo, Time
 from pyflink.common.typeinfo import _from_java_type, TypeInformation, 
_is_instance_of
 from pyflink.java_gateway import get_gateway
-from pyflink.table import Table, StreamTableEnvironment
+from pyflink.table import Table, StreamTableEnvironment, Expression
 from pyflink.util.java_utils import to_jarray
 
 from pyflink.ml.core.api import Model, Transformer, AlgoOperator, Stage, 
Estimator
@@ -360,3 +360,12 @@ def _to_java_tables(*inputs: Table):
     """
     gateway = get_gateway()
     return to_jarray(gateway.jvm.org.apache.flink.table.api.Table, [t._j_table 
for t in inputs])
+
+
+def call_java_table_function(java_table_function_name: str, *args):
+    _function = get_gateway().jvm
+    for member_name in java_table_function_name.split('.'):
+        _function = _function.__getattr__(member_name)
+    return Expression(_function(to_jarray(
+        get_gateway().jvm.java.lang.Object,
+        [expression._j_expr for expression in args])))
diff --git a/flink-ml-python/pyflink/ml/lib/functions.py 
b/flink-ml-python/pyflink/ml/lib/functions.py
new file mode 100644
index 0000000..1468834
--- /dev/null
+++ b/flink-ml-python/pyflink/ml/lib/functions.py
@@ -0,0 +1,34 @@
+################################################################################
+#  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.
+################################################################################
+from pyflink.ml.core.wrapper import call_java_table_function
+from pyflink.table import Expression
+
+
+def vector_to_array(*args) -> Expression:
+    """
+    Converts a column of :class:`Vector`s into a column of double arrays.
+    """
+    return 
call_java_table_function('org.apache.flink.ml.Functions.vectorToArray', *args)
+
+
+def array_to_vector(*args) -> Expression:
+    """
+    Converts a column of arrays of numeric type into a column of
+    :class:`DenseVector` instances.
+    """
+    return 
call_java_table_function('org.apache.flink.ml.Functions.arrayToVector', *args)
diff --git a/flink-ml-python/pyflink/ml/lib/tests/test_functions.py 
b/flink-ml-python/pyflink/ml/lib/tests/test_functions.py
new file mode 100644
index 0000000..6ad059b
--- /dev/null
+++ b/flink-ml-python/pyflink/ml/lib/tests/test_functions.py
@@ -0,0 +1,114 @@
+################################################################################
+#  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.
+################################################################################
+from pyflink.common import Types
+from pyflink.ml.core.linalg import Vectors, DenseVectorTypeInfo, 
SparseVectorTypeInfo, \
+    VectorTypeInfo
+from pyflink.ml.lib.functions import vector_to_array, array_to_vector
+from pyflink.ml.tests.test_utils import PyFlinkMLTestCase
+from pyflink.table.expressions import col
+
+
+class FunctionsTest(PyFlinkMLTestCase):
+    def setUp(self):
+        super(FunctionsTest, self).setUp()
+
+        self.double_arrays = [
+            ([0.0, 0.0],),
+            ([0.0, 1.0],),
+        ]
+
+        self.float_arrays = [
+            ([float(0.0), float(0.0)],),
+            ([float(0.0), float(1.0)],),
+        ]
+
+        self.int_arrays = [
+            ([0, 0],),
+            ([0, 1],),
+        ]
+
+        self.dense_vectors = [
+            (Vectors.dense(0.0, 0.0),),
+            (Vectors.dense(0.0, 1.0),),
+        ]
+
+        self.sparse_vectors = [
+            (Vectors.sparse(2, [], []),),
+            (Vectors.sparse(2, [1], [1.0]),),
+        ]
+
+        self.mixed_vectors = [
+            (Vectors.dense(0.0, 0.0),),
+            (Vectors.sparse(2, [1], [1.0]),),
+        ]
+
+    def test_vector_to_array(self):
+        self._test_vector_to_array(self.dense_vectors, DenseVectorTypeInfo())
+        self._test_vector_to_array(self.sparse_vectors, SparseVectorTypeInfo())
+        self._test_vector_to_array(self.mixed_vectors, VectorTypeInfo())
+
+    def _test_vector_to_array(self, vectors, vector_type_info):
+        input_table = self.t_env.from_data_stream(
+            self.env.from_collection(vectors,
+                                     type_info=Types.ROW_NAMED(
+                                         ['vector'],
+                                         [vector_type_info])
+                                     ))
+
+        output_table = 
input_table.select(vector_to_array(col('vector')).alias('array'))
+
+        output_values = [x['array'] for x in 
self.t_env.to_data_stream(output_table)
+                         .map(lambda r: r).execute_and_collect()]
+
+        self.assertEqual(len(output_values), len(self.double_arrays))
+
+        output_values.sort(key=lambda x: x[1])
+
+        for i in range(len(self.double_arrays)):
+            self.assertEqual(self.double_arrays[i][0], output_values[i])
+
+    def test_array_to_vector(self):
+        self._test_array_to_vector(self.double_arrays, Types.DOUBLE())
+        self._test_array_to_vector(self.float_arrays, Types.FLOAT())
+        self._test_array_to_vector(self.int_arrays, Types.INT())
+        self._test_array_to_vector(self.int_arrays, Types.LONG())
+
+    def _test_array_to_vector(self, arrays, array_element_type_info):
+        input_table = self.t_env.from_data_stream(
+            self.env.from_collection(
+                arrays,
+                type_info=Types.ROW_NAMED(
+                    ['array'],
+                    [Types.PRIMITIVE_ARRAY(array_element_type_info)]
+                )
+            )
+        )
+
+        output_table = 
input_table.select(array_to_vector(col('array')).alias('vector'))
+
+        field_names = output_table.get_schema().get_field_names()
+
+        output_values = [x[field_names.index('vector')] for x in
+                         
self.t_env.to_data_stream(output_table).execute_and_collect()]
+
+        self.assertEqual(len(output_values), len(self.dense_vectors))
+
+        output_values.sort(key=lambda x: x.get(1))
+
+        for i in range(len(self.dense_vectors)):
+            self.assertEqual(self.dense_vectors[i][0], output_values[i])


Reply via email to