web-dev-qa-db-fra.com

Construire un StructType à partir d'une trame de données dans pyspark

Je suis nouveau spark et python et je suis confronté à cette difficulté de construction d'un schéma à partir d'un fichier de métadonnées qui peut être appliqué à mon fichier de données. Scénario: Fichier de métadonnées pour le fichier Data (format csv), contient les colonnes et leurs types: par exemple:

id,int,10,"","",id,"","",TRUE,"",0
created_at,timestamp,"","","",created_at,"","",FALSE,"",0

J'ai réussi à convertir cela en une trame de données qui ressemble à:

+--------------------+---------------+
|                name|           type|
+--------------------+---------------+
|                  id|  IntegerType()|
|          created_at|TimestampType()|
|          updated_at|   StringType()|

Mais quand j'essaie de convertir cela en un format StructField en utilisant ce

fields = schemaLoansNew.map(lambda l:([StructField(l.name, l.type, 'true')]))

OR

schemaList = schemaLoansNew.map(lambda l: ("StructField(" + l.name + "," + l.type + ",true)")).collect()

Et ensuite, convertissez-le en StructType, en utilisant

schemaFinal = StructType(schemaList)

J'obtiens l'erreur suivante:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/mapr/spark/spark-1.4.1/python/pyspark/sql/types.py", line 372, in __init__
assert all(isinstance(f, DataType) for f in fields), "fields should be a list of DataType"
AssertionError: fields should be a list of DataType

Je suis coincé à ce sujet en raison de mon manque de connaissances sur les trames de données, pouvez-vous s'il vous plaît indiquer comment procéder à ce sujet. une fois le schéma prêt, je souhaite utiliser createDataFrame pour appliquer à mon fichier de données. Ce processus doit être fait pour de nombreuses tables, donc je ne veux pas coder en dur les types plutôt utiliser le fichier de métadonnées pour construire le schéma et ensuite appliquer au RDD.

Merci d'avance.

11
learning

Les champs dont l'argument doit être une liste d'objets DataType. Cette:

.map(lambda l:([StructField(l.name, l.type, 'true')]))

génère après collect a list de lists de tuples (Rows) de DataType (list[list[Tuple[DataType]]]) sans mentionner que l'argument nullable doit être booléen et non une chaîne.

Votre deuxième tentative:

.map(lambda l: ("StructField(" + l.name + "," + l.type + ",true)")).

génère après collect un list des str objets.

Le schéma correct pour l'enregistrement que vous avez montré devrait ressembler plus ou moins à ceci:

from pyspark.sql.types import *

StructType([
    StructField("id", IntegerType(), True),
    StructField("created_at", TimestampType(), True),
    StructField("updated_at", StringType(), True)
])

Bien que l'utilisation de structures de données distribuées pour une tâche comme celle-ci soit une exagération grave, sans parler de l'inefficacité, vous pouvez essayer d'ajuster votre première solution comme suit:

StructType([
    StructField(name, eval(type), True) for (name, type) in  df.rdd.collect()
])

mais ce n'est pas particulièrement sûr (eval). Il pourrait être plus facile de créer un schéma à partir de JSON/dictionnaire. En supposant que vous ayez une fonction qui mappe de la description de type au nom de type canonique:

def get_type_name(s: str) -> str:
    """
    >>> get_type_name("int")
    'integer'
    """
    _map = {
        'int': IntegerType().typeName(),
        'timestamp': TimestampType().typeName(),
        # ...
    } 
    return _map.get(s, StringType().typeName())

Vous pouvez créer un dictionnaire de la forme suivante:

schema_dict = {'fields': [
    {'metadata': {}, 'name': 'id', 'nullable': True, 'type': 'integer'},
    {'metadata': {}, 'name': 'created_at', 'nullable': True, 'type': 'timestamp'}
], 'type': 'struct'}

et le nourrir à StructType.fromJson:

StructType.fromJson(schema_dict)
21
zero323
val columns: Array[String] = df1.columns
val reorderedColumnNames: Array[String] = df2.columns //or do the reordering you want
val result: DataFrame = dataFrame.select(reorderedColumnNames.head, reorderedColumnNames.tail: _*)
1
Andy Quiroz

Les étapes ci-dessous peuvent être suivies pour modifier les objets de type de données

data_schema=[
    StructField("age", IntegerType(), True),
    StructField("name", StringType(), True)
]



final_struct=StructType(fields=data_schema)

df=spark.read.json('/home/abcde/Python-and-Spark-for-Big-Data-master/Spark_DataFrames/people.json', schema=final_struct)



df.printSchema()

root
 |-- age: integer (nullable = true)
 |-- name: string (nullable = true)
0
BigData-Guru