从Spark DataFrame中选择特定列

时间:2018-08-04 20:48:30

标签: scala apache-spark apache-spark-sql

我已将CSV数据加载到Spark DataFrame中。

我需要将此数据帧切成两个不同的数据帧,其中每个数据帧都包含原始数据帧中的一组列。

如何基于列选择Spark数据框中的子集?

8 个答案:

答案 0 :(得分:3)

如果要将数据帧分为两个不同的数据帧,请在其上用所需的不同列进行两个选择。

 val sourceDf = spark.read.csv(...)
 val df1 = sourceDF.select(first set of columns)
 val df2 = sourceDF.select(second set of columns)

请注意,这当然意味着对sourceDf进行两次评估,因此,如果它可以放入分布式内存中,并且您在两个数据帧中都使用了大多数列,则缓存它是一个好主意。它有许多不需要的多余列,然后您可以先对其进行选择,然后再选择需要的列,这样它将所有这些多余的数据存储在内存中。

答案 1 :(得分:2)

假设我们的父数据框具有'n'

我们可以创建'x'个子DataFrames(在我们的示例中为2)。

可以根据需要从任何父Dataframe列中选择子Dataframe的列。

考虑者源具有 10列,我们想分为 2个数据框,其中包含从父数据框引用的列。

可以使用 select Dataframe API

确定子Dataframe的列
val parentDF = spark.read.format("csv").load("/path of the CSV file")

val Child1_DF = parentDF.select("col1","col2","col3","col9","col10").show()

val child2_DF = parentDF.select("col5", "col6","col7","col8","col1","col2").show()

请注意,子数据帧中的列数长度可以不同,并且小于父数据帧中的列数。

我们还可以使用父数据帧中所需列的位置索引来引用列名,而不必提及真实名称

首先导入spark隐式对象,它用作使用$表示法通过位置索引访问列的辅助类

import spark.implicits._
import org.apache.spark.sql.functions._

val child3_DF  = parentDF.select("_c0","_c1","_c2","_c8","_c9").show()

我们还可以根据特定条件选择列。假设我们只希望在子数据框中选择偶数列。通过偶数,我们指的是偶数索引列,并且索引从“ 0”开始

val parentColumns = parentDF.columns.toList


res0: List[String] = List(_c0, _c1, _c2, _c3, _c4, _c5, _c6, _c7,_c8,_c9)

val evenParentColumns =  res0.zipWithIndex.filter(_._2 % 2 == 0).map( _._1).toSeq

res1: scala.collection.immutable.Seq[String] = List(_c0, _c2, _c4, _c6,_c8)

现在将这些列提供要从parentDF中选择。请注意,select API需要seq类型参数。因此我们将“ evenParentColumns”转换为Seq集合

val child4_DF = parentDF.select(res1.head, res1.tail:_*).show()

这将显示父数据框的偶数索引列。


| _c0 | _c2 | _c4 | _c6 | _c8 |


| ITE00100554 | TMAX |空| E | 1 |

| TE00100554 | TMIN |空| E | 4 |

| GM000010962 | PRCP |空| E | 7 |

所以现在我们剩下数据帧中的偶数列

类似地,我们还可以将其他操作应用于“数据框”列,如下所示

val child5_DF = parentDF.select($"_c0", $"_c8" + 1).show()

因此,通过前面提到的许多方法,我们可以选择“数据框”中的列。

答案 2 :(得分:2)

有多个选项(特别是在Scala中)选择该Dataframe的列的子集。以下行将全部选择两列colAcolB

import spark.implicits._
import org.apache.spark.sql.functions.{col, column, expr}

inputDf.select(col("colA"), col("colB"))
inputDf.select(inputDf.col("colA"), inputDf.col("colB"))
inputDf.select(column("colA"), column("colB"))
inputDf.select(expr("colA"), expr("colB"))

// only available in Scala
inputDf.select($"colA", $"colB")
inputDf.select('colA, 'colB) // makes use of Scala's Symbol

// selecting columns based on a given iterable of Strings
val selectedColumns: Seq[Column] = Seq("colA", "colB").map(c => col(c))
inputDf.select(selectedColumns: _*)

// select the first or last 2 columns
inputDf.selectExpr(inputDf.columns.take(2): _*)
inputDf.selectExpr(inputDf.columns.takeRight(2): _*)

使用$是可能的,因为Scala提供了一个隐式类,该类使用方法$将字符串转换为列:

implicit class StringToColumn(val sc : scala.StringContext) extends scala.AnyRef {
  def $(args : scala.Any*) : org.apache.spark.sql.ColumnName = { /* compiled code */ }
}

通常,当您想将一个DataFrame派生到多个DataFrame时,如果您在创建其他DataFrame之前先persist,则可能会提高性能。最后,您可以unpersist原始DataFrame。

请记住,不会在编译时解析,只有在将其与在查询执行的分析器阶段发生的目录的列名进行比较时才会解析。如果您需要更强的类型安全性,则可以创建一个Dataset

为完整起见,下面是csv尝试上述代码:

// csv file:
// colA,colB,colC
// 1,"foo","bar"

val inputDf = spark.read.format("csv").option("header", "true").load(csvFilePath)

// resulting DataFrame schema
root
 |-- colA: string (nullable = true)
 |-- colB: string (nullable = true)
 |-- colC: string (nullable = true)

答案 3 :(得分:1)

我喜欢除杂方法,因为它允许我一步选择,重命名和转换列。但是我必须对其进行调整以使其在PySpark中对我有用:

from pyspark.sql.functions import col

spark.read.csv(path).select(
      col('_c0').alias("stn").cast('String'),
      col('_c1').alias("wban").cast('String'),
      col('_c2').alias("lat").cast('Double'),
      col('_c3').alias("lon").cast('Double')
    )
      .where('_c2.isNotNull && '_c3.isNotNull && '_c2 =!= 0.0 && '_c3 =!= 0.0)

答案 4 :(得分:0)

已解决, 只需对数据框使用 select 方法以选择列:

 val df=spark.read.csv("C:\\Users\\Ahmed\\Desktop\\cabs_trajectories\\cabs_trajectories\\green\\2014\\green_tripdata_2014-09.csv")

val df1=df.select("_c0")

这将细分数据框的第一列

答案 5 :(得分:0)

只需使用选择select,您就可以选择特定的列,为它们指定可读的名称并进行转换。例如这样的:

spark.read.csv(path).select(
          '_c0.alias("stn").cast(StringType),
          '_c1.alias("wban").cast(StringType),
          '_c2.alias("lat").cast(DoubleType),
          '_c3.alias("lon").cast(DoubleType)
        )
          .where('_c2.isNotNull && '_c3.isNotNull && '_c2 =!= 0.0 && '_c3 =!= 0.0)

答案 6 :(得分:0)

问题是在与其他联接之后选择数据框上的列 数据框。

我在下面尝试过,并从联接中选择薪金Df的列 数据框。

希望这会有所帮助

        val empDf=spark.read.option("header","true").csv("/data/tech.txt")

        val salaryDf=spark.read.option("header","true").csv("/data/salary.txt")

        val joinData= empDf.join(salaryDf,empDf.col("first") === salaryDf.col("first") and  empDf.col("last") === salaryDf.col("last"))

      //**below will select the colums of salaryDf only**

     val finalDF=joinData.select(salaryDf.columns map  salaryDf.col:_*)

//same way we can select the columns of empDf
joinData.select(empDf.columns map  empDf.col:_*)

答案 7 :(得分:-1)

您可以使用以下代码根据列的索引(位置)选择列。您可以更改变量colNos的数字以仅选择那些列

import org.apache.spark.sql.functions.col

val colNos = Seq(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35)
val Df_01 = Df.select(colNos_01 map Df.columns map col: _*)
Df_01.show(20, false)