создавать столбцы на основе элементов groupby, используя sql, pandas или pyspark? - PullRequest
1 голос
/ 18 июня 2020

Это мои данные:

| ID | Date  |
| 1  | 20-Mar|
| 1  | 30-Mar|
| 1  | 20-Apr|
| 2  | 10-Mar|
| 2  | 12-Mar|
| 3  | 20-Mar|
| 4  | 20-Mar|
| 4  | 9-Mar |

Я хочу сгруппировать по идентификаторам и создать новые столбцы с такими датами:

| ID | Date_1 | Date_2 | Date_3  | Date_4  |
| 1  | 20-Mar | 30-Mar | 20-Apr  |
| 2  | 10-Mar | 12-Mar |
| 3  | 20-Mar |
| 4  | 9-Mar  | 20-Mar |

Спасибо

Ответы [ 4 ]

2 голосов
/ 18 июня 2020

Используйте функции groupBy, collect_list, затем дату создания _ * столбцов на основе значения array_index.

Example:

#sample dataframe
df.show()
#+---+------+
#| ID|  Date|
#+---+------+
#|  1|20-Mar|
#|  1|30-Mar|
#|  1|20-Apr|
#|  2|10-Mar|
#+---+------+
from pyspark.sql.functions import *

df.groupBy("id").agg(collect_list(col("Date")).alias("tmp")).\
withColumn("Date_1",col("tmp")[0]).\
withColumn("Date_2",col("tmp")[1]).\
withColumn("Date_3",col("tmp")[2]).\
withColumn("Date_4",col("tmp")[3]).\
drop("tmp").\
show(10,False)

#+---+------+------+------+------+
#|id |Date_1|Date_2|Date_3|Date_4|
#+---+------+------+------+------+
#|1  |20-Mar|30-Mar|20-Apr|null  |
#|2  |10-Mar|null  |null  |null  |
#+---+------+------+------+------+

Начиная с Spark-2.4 используйте element_at функцию:

df.groupBy("id").agg(collect_list(col("Date")).alias("tmp")).\
withColumn("Date_1",element_at(col("tmp"),1)).\
withColumn("Date_2",element_at(col("tmp"),2)).\
withColumn("Date_3",element_at(col("tmp"),3)).\
withColumn("Date_4",element_at(col("tmp"),4)).\
drop("tmp").\
show(10,False)

Dynamic way:

df1=df.groupBy(col("id")).agg(collect_list(col("date")))

#get the max size of array
size=df.groupBy("id").agg(collect_list(col("Date")).alias("tmp")).select(max(size("tmp"))).collect()[0][0]

df1.select([df1.id]+ [df1.tmp[i].alias("date_"+ str(i+1)) for i in range(size+1)]).\
show()
#+---+------+------+------+------+
#| id|date_1|date_2|date_3|date_4|
#+---+------+------+------+------+
#|  1|20-Mar|30-Mar|20-Apr|  null|
#|  2|10-Mar|  null|  null|  null|
#+---+------+------+------+------+
0 голосов
/ 18 июня 2020

Можно попробовать вот так

df = df.groupby('ID').apply(lambda x:x.assign(flag=["Date"+str(s+1) for s in range(len(x))])).reset_index(drop=True)
res = df.pivot(index='ID', columns='flag', values='Date')
print(res)
0 голосов
/ 18 июня 2020
  • Загрузите фрейм данных
  • Группируйте по ID
  • Собирайте даты как массив DateArr
  • Запустите выбор ID, который динамически добавляет столбец с использованием map (доступно в Python + Scala)
  • Если вы хотите сделать свой код более независимым от no. столбцов, затем отметьте Solution 2
import org.apache.spark.sql.functions._

object ArrayToColumns {

  def main(args: Array[String]): Unit = {

    val spark = Constant.getSparkSess

    import spark.implicits._

    val df = List( (1,"20-Mar"),(1,"30-Mar"),(1,"20-Mar"),
      (2,"10-Mar"),(2,"12-Mar"),
      (3,"20-Mar"),
      (4,"20-Mar"),(4,"09-Mar")
    ).toDF("ID","Date")

    df.groupBy("ID")
      .agg(collect_list("Date").as("DateArr"))
      .select(col("ID") +: (0 until 4).map(i => coalesce(col("DateArr")(i),lit("")).alias(s"Date_${i+1}")): _*)
      .show()

  }

}

Решение 2

    val dfNew = df.groupBy("ID")
      .agg(collect_list("Date").as("DateArr"))

    val maxArraySize : Int = dfNew.select(max(size(col("DateArr")).as("ArraySize"))).head().getInt(0)

    dfNew
      .select(col("ID") +: (0 until maxArraySize).map(i => coalesce(col("DateArr")(i),lit("")).alias(s"Date_${i+1}")): _*)
      .show()
0 голосов
/ 18 июня 2020

В SQL это можно сделать с помощью row_number() и условного агрегирования:

select
    id,
    max(case when rn = 1 then date end) date_1,
    max(case when rn = 2 then date end) date_2,
    max(case when rn = 3 then date end) date_3,
    max(case when rn = 4 then date end) date_4
from (
    select t.*, row_number() over(partition by id order by date) rn
    from mytable t
) t
group by id
...