Intenta interpretar el código de forma correcta antes de adaptarlo a tu proyecto y si tdeseas aportar algo puedes dejarlo en los comentarios.
Solución:
Desde Spark 2.4 puedes usar slice
función. En Pitón):
pyspark.sql.functions.slice(x, start, length)
Función de colección: devuelve un array que contiene todos los elementos en x desde el inicio del índice (o desde el final si el inicio es negativo) con la longitud especificada.
…
Nuevo en la versión 2.4.
from pyspark.sql.functions import slice
df = spark.createDataFrame([
(10, "Finance", ["Jon", "Snow", "Castle", "Black", "Ned"]),
(20, "IT", ["Ned", "is", "no", "more"])
], ("dept_id", "dept_nm", "emp_details"))
df.select(slice("emp_details", 1, 3).alias("empt_details")).show()
+-------------------+
| empt_details|
+-------------------+
|[Jon, Snow, Castle]|
| [Ned, is, no]|
+-------------------+
En Scala
def slice(x: Column, start: Int, length: Int): Column
Devuelve un array que contiene todos los elementos en x desde el inicio del índice (o desde el final si el inicio es negativo) con la longitud especificada.
import org.apache.spark.sql.functions.slice
val df = Seq(
(10, "Finance", Seq("Jon", "Snow", "Castle", "Black", "Ned")),
(20, "IT", Seq("Ned", "is", "no", "more"))
).toDF("dept_id", "dept_nm", "emp_details")
df.select(slice($"emp_details", 1, 3) as "empt_details").show
+-------------------+
| empt_details|
+-------------------+
|[Jon, Snow, Castle]|
| [Ned, is, no]|
+-------------------+
Por supuesto, se puede hacer lo mismo en SQL
SELECT slice(emp_details, 1, 3) AS emp_details FROM df
Importante:
Tenga en cuenta que, a diferencia de Seq.slice
, los valores se indexan desde cero y el segundo argumento es la longitud, no la posición final.
Aquí hay una solución que utiliza una función definida por el usuario que tiene la ventaja de funcionar para cualquier tamaño de rebanada que desee. Simplemente construye una función UDF alrededor del scala incorporado. slice
método:
import sqlContext.implicits._
import org.apache.spark.sql.functions._
val slice = udf((array : Seq[String], from : Int, to : Int) => array.slice(from,to))
Ejemplo con una muestra de sus datos:
val df = sqlContext.sql("select array('Jon', 'Snow', 'Castle', 'Black', 'Ned') as emp_details")
df.withColumn("slice", slice($"emp_details", lit(0), lit(3))).show
Produce la salida esperada
+--------------------+-------------------+
| emp_details| slice|
+--------------------+-------------------+
|[Jon, Snow, Castl...|[Jon, Snow, Castle]|
+--------------------+-------------------+
También puede registrar la UDF en su sqlContext
y úsalo así
sqlContext.udf.register("slice", (array : Seq[String], from : Int, to : Int) => array.slice(from,to))
sqlContext.sql("select array('Jon','Snow','Castle','Black','Ned'),slice(array('Jon','Snow','Castle','Black','Ned'),0,3)")
No necesitaras lit
más con esta solución
Edit2: Para quién quiere evitar udf a expensas de la legibilidad 😉
Si realmente desea hacerlo en un solo paso, tendrá que usar Scala para crear una función lambda que devuelva una secuencia de Column
y envuélvelo en un array. Esto es un poco complicado, pero es un paso:
val df = List(List("Jon", "Snow", "Castle", "Black", "Ned")).toDF("emp_details")
df.withColumn("slice", array((0 until 3).map(i => $"emp_details"(i)):_*)).show(false)
+-------------------------------+-------------------+
|emp_details |slice |
+-------------------------------+-------------------+
|[Jon, Snow, Castle, Black, Ned]|[Jon, Snow, Castle]|
+-------------------------------+-------------------+
los _:*
funciona un poco de magia para pasar una lista a una función llamada variadic (array
en este caso, que construyen el sql array). Pero aconsejaría no usar esta solución tal como está. poner la función lambda en una función nombrada
def slice(from: Int, to: Int) = array((from until to).map(i => $"emp_details"(i)):_*))
para la legibilidad del código. Tenga en cuenta que, en general, seguir Column
Las expresiones (sin usar `udf) tienen mejores resultados.
Editar: para hacerlo en una declaración sql (como pregunta en su pregunta …), siguiendo la misma lógica, generaría la consulta sql usando la lógica scala (sin decir que sea la más legible)
def sliceSql(emp_details: String, from: Int, to: Int): String = "Array(" + (from until to).map(i => "emp_details["+i.toString+"]").mkString(",") + ")"
val sqlQuery = "select emp_details,"+ sliceSql("emp_details",0,3) + "as slice from emp_details"
sqlContext.sql(sqlQuery).show
+-------------------------------+-------------------+
|emp_details |slice |
+-------------------------------+-------------------+
|[Jon, Snow, Castle, Black, Ned]|[Jon, Snow, Castle]|
+-------------------------------+-------------------+
tenga en cuenta que puede reemplazar until
por to
para proporcionar el último elemento tomado en lugar del elemento en el que se detiene la iteración.
Puedes añadir valor a nuestro contenido dando tu veteranía en las reseñas.