Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a vector column in a dataframe back into an array column

I have a dataframe with two columns one of which (called dist) is a dense vector. How can I convert it back into an array column of integers.

+---+-----+
| id| dist| 
+---+-----+
|1.0|[2.0]|
|2.0|[4.0]|
|3.0|[6.0]|
|4.0|[8.0]|
+---+-----+

I tried using several variants of the following udf but it returns a type mismatch error

val toInt4 = udf[Int, Vector]({ (a) => (a)})  

val result = df.withColumn("dist", toDf4(df("dist"))).select("dist")
like image 893
ulrich Avatar asked Mar 07 '16 22:03

ulrich


People also ask

How do I turn a column into a DataFrame in an array?

You can convert select columns of a dataframe into an numpy array using the to_numpy() method by passing the column subset of the dataframe. For example, df[['Age']] will return just the age column.

How do you convert a DataFrame to a matrix in python?

A two-dimensional rectangular array to store data in rows and columns is called python matrix. Matrix is a Numpy array to store data in rows and columns. Using dataframe. to_numpy() method we can convert dataframe to Numpy Matrix.


3 Answers

I struggled for a while to get the answer from @ThomasLuechtefeld working. But was running into this very frustrating error:

org.apache.spark.sql.AnalysisException: cannot resolve 'UDF(features_scaled)' due to data type mismatch: argument 1 requires vector type, however, '`features_scaled`' is of vector type.

Turns out I needed to import DenseVector from the ml package instead of the mllib package.

So this worked for me:

import org.apache.spark.ml.linalg.DenseVector
import org.apache.spark.sql.functions._

val vectorToColumn = udf{ (x:DenseVector, index: Int) => x(index) }
myDataframe.withColumn("clusters_scaled",vectorToColumn(col("features_scaled"),lit(0)))

Yes, the only difference is that first line. This should absolutely be a comment, but I don't have the reputation. Sorry!

like image 65
pwb2103 Avatar answered Nov 14 '22 20:11

pwb2103


I think it's easiest to do it by going to the RDD API and then back.

import org.apache.spark.mllib.linalg.DenseVector
import org.apache.spark.sql.DataFrame
import org.apache.spark.rdd.RDD
import sqlContext._

// The original data.
val input: DataFrame =
  sc.parallelize(1 to 4)
    .map(i => i.toDouble -> new DenseVector(Array(i.toDouble * 2)))
    .toDF("id", "dist")

// Turn it into an RDD for manipulation.
val inputRDD: RDD[(Double, DenseVector)] =
  input.map(row => row.getAs[Double]("id") -> row.getAs[DenseVector]("dist"))

// Change the DenseVector into an integer array.
val outputRDD: RDD[(Double, Array[Int])] =
  inputRDD.mapValues(_.toArray.map(_.toInt))

// Go back to a DataFrame.
val output = outputRDD.toDF("id", "dist")
output.show

You get:

+---+----+
| id|dist|
+---+----+
|1.0| [2]|
|2.0| [4]|
|3.0| [6]|
|4.0| [8]|
+---+----+
like image 43
Daniel Darabos Avatar answered Nov 14 '22 21:11

Daniel Darabos


In spark 2.0 you can do something like:

import org.apache.spark.mllib.linalg.DenseVector
import org.apache.spark.sql.functions.udf

val vectorHead = udf{ x:DenseVector => x(0) }
df.withColumn("firstValue", vectorHead(df("vectorColumn")))
like image 36
Thomas Luechtefeld Avatar answered Nov 14 '22 21:11

Thomas Luechtefeld