web-dev-qa-db-fra.com

PySpark ajouter une colonne à un DataFrame à partir d'une colonne TimeStampType

J'ai un DataFrame qui ressemble à quelque chose comme ça. Je souhaite opérer le jour du date_time champ.

root
 |-- Host: string (nullable = true)
 |-- user_id: string (nullable = true)
 |-- date_time: timestamp (nullable = true)

J'ai essayé d'ajouter une colonne pour extraire la journée. Jusqu'à présent, mes tentatives ont échoué.

df = df.withColumn("day", df.date_time.getField("day"))

org.Apache.spark.sql.AnalysisException: GetField is not valid on fields of type TimestampType;

Cela a également échoué

df = df.withColumn("day", df.select("date_time").map(lambda row: row.date_time.day))

AttributeError: 'PipelinedRDD' object has no attribute 'alias'

Une idée de comment cela peut être fait?

18
Wai Yip Tung

Vous pouvez utiliser un simple map:

df.rdd.map(lambda row:
    Row(row.__fields__ + ["day"])(row + (row.date_time.day, ))
)

Une autre option consiste à enregistrer une fonction et à exécuter une requête SQL:

sqlContext.registerFunction("day", lambda x: x.day)
sqlContext.registerDataFrameAsTable(df, "df")
sqlContext.sql("SELECT *, day(date_time) as day FROM df")

Enfin, vous pouvez définir udf comme ceci:

from pyspark.sql.functions import udf
from pyspark.sql.types import IntegerType

day = udf(lambda date_time: date_time.day, IntegerType())
df.withColumn("day", day(df.date_time))

[~ # ~] éditez [~ # ~] :

En fait, si vous utilisez la fonction brute SQL day est déjà définie (au moins dans Spark 1.4), vous pouvez donc omettre l'enregistrement udf. Il fournit également un certain nombre de fonctions de traitement de date différentes, y compris :

Il est également possible d'utiliser des expressions de date simples comme:

current_timestamp() - expr("INTERVAL 1 HOUR")

Cela signifie que vous pouvez créer des requêtes relativement complexes sans passer de données à Python. Par exemple:

df =  sc.parallelize([
    (1, "2016-01-06 00:04:21"),
    (2, "2016-05-01 12:20:00"),
    (3, "2016-08-06 00:04:21")
]).toDF(["id", "ts_"])

now = lit("2016-06-01 00:00:00").cast("timestamp") 
five_months_ago = now - expr("INTERVAL 5 MONTHS")

(df
    # Cast string to timestamp
    # For Spark 1.5 use cast("double").cast("timestamp")
    .withColumn("ts", unix_timestamp("ts_").cast("timestamp"))
    # Find all events in the last five months
    .where(col("ts").between(five_months_ago, now))
    # Find first Sunday after the event
    .withColumn("next_sunday", next_day(col("ts"), "Sun"))
    # Compute difference in days
    .withColumn("diff", datediff(col("ts"), col("next_sunday"))))
33
zero323