Marco de datos Pyspark con columna XML y múltiples valores dentro: Extraer columnas fuera de ella

Tengo un marco de datos de pyspark donde una columna tiene un XML dentro. Cada XML en una fila parece así, algunos tienen 2 entradas, unos 3 y 4:

Ejemplo de una entrada de fila:

xml version="1.0" encoding="utf-8"?      

Tengo que analizar los valores fuera de ella para el objetivo id, nombre y sexo y crear columnas fuera de él.

Dado que un XML puede tener varias entradas, es difícil generar un número fijo de columnas de él. Mi idea era crear una columna para cada uno de estos atributos (medios añadir 3 columnas al marco de datos), donde entonces las listas están dentro.

en este ejemplo expande el marco de datos pyspark con columnas:

gol idNombresexo
[445,2468,4334,15][xxxy,qwerzui,foo,fooh][F,F,M,F]

Estaba pensando en un UDF que itera a través de la columna XML y crea las columnas de ella. ¿Qué piensas de esto, tiene sentido hacerlo de esta manera? Para poder analizar más adelante. En realidad no es común tener listas en columnas.

Lo intenté a través de:

import xml.etree.ElementTree as ET

root = ET.fromstring(string)

y con lo siguiente puedo acceder a los valores dentro, pero no soy capaz de ponerlo en una función de udf adecuada para ampliar mi marco de datos de pyspark.

for child in root:
  print(child.tag, child.attrib)
  
for child in root:
  print(child.attrib['age'],child.attrib['sex']) 

Desafortunadamente, las otras soluciones de la apiloverflow no podrían ayudarme, así que espero una solución para mi problema

Pregunta hecha hace 3 años, 4 meses, 26 días - Por techwizkid


4 Respuestas:

  • Usa xpath. No es necesario utilizar UDF y debe dar un mejor rendimiento.

    df2 = df.selectExpr(
        ["xpath(col, 'goals/goal/@%s') as %s" % (c,c) for c in ['id', 'name', 'sex']]
    )
    
    df2.show(20,0)
    +---------------------+--------------------------+------------+
    |id                   |name                      |sex         |
    +---------------------+--------------------------+------------+
    |[445, 2468, 4334, 15]|[xxxy, qwerzui, foo, fooh]|[F, F, M, F]|
    +---------------------+--------------------------+------------+
    

    Si quieres añadirlas como nuevas columnas, haz

    df2 = df.selectExpr('*',
        *["xpath(col, 'goals/goal/@%s') as %s" % (c,c) for c in ['id', 'name', 'sex']]
    )
    

    Respondida el Dec 19, 2020 a las 13:47 - por Kairo

    Votos positivos: 0 | Votos negativos: 0

  • El siguiente código genera las 3 listas

    import xml.etree.ElementTree as ET
    
    XML = '''xml version="1.0" encoding="utf-8"?   
                                                              
                                                             
                                                    
    '''
    final = []
    attributes = ['id', 'name', 'sex']
    root = ET.fromstring(XML)
    for attrib in attributes:
        final.append([g.attrib[attrib] for g in root.findall('goal')])
    print(final)
    

    Producto

    [['445', '2468', '4334', '15'], ['xxxy', 'qwerzui', 'foo', 'fooh'], ['F', 'F', 'M', 'F']]
    

    Respondida el Dec 19, 2020 a las 13:53 - por codesculptor3f6e

    Votos positivos: 0 | Votos negativos: 0

  • Para lograr esto en PySpark, puedes usar la función explode() para transformar las múltiples entradas dentro de la columna XML en filas individuales y luego pivotear las columnas para obtener la estructura deseada. Aquí te muestro un ejemplo de cómo puedes hacerlo:

    python
    from pyspark.sql import SparkSession
    from pyspark.sql.functions import explode, col
    
    

    Crear una Spark session

    spark = SparkSession.builder.appName("XML parsing").getOrCreate()

    Crear un DataFrame de ejemplo con una columna 'xml_column' que contiene XML

    data = [("xml version='1.0' encoding='utf-8' ",), ("xml version='1.0' encoding='utf-8' ",)] df = spark.createDataFrame(data, ["xml_column"])

    Definir una función para extraer los atributos del XML y convertirlos en una fila

    def extract_attributes(row): root = ET.fromstring(row.xml_column) attributes = [] for child in root: attributes.append((child.attrib['id'], child.attrib['name'], child.attrib['sex'])) return attributes

    Registrando la función como un UDF

    spark.udf.register("extract_attributes", extract_attributes)

    Aplicar la función a cada fila del DataFrame

    df = df.withColumn("attributes", explode(extract_attributes(col(""))))

    Pivoteando las columnas para obtener la estructura deseada

    df = df.selectExpr("attributes[0] as goal_id", "attributes[1] as name", "attributes[2] as sex") df.show()

    Con este código, puedes extraer los atributos id, nombre y sexo del XML contenido en la columna 'xmlcolumn', expandirlos en filas individuales y luego pivotearlos en las columnas goalid, name y sex.

    Espero que esto te ayude a resolver tu problema. ¡Déjame saber si tienes alguna otra pregunta!

    Respondida el Dec 20, 2020 a las 13:43 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Uso de un UDF para Analizar Datos XML

    Sí, utilizar un UDF (Función Definida por el Usuario) es un enfoque adecuado para analizar los datos XML dentro de una columna de DataFrame y crear columnas adicionales fuera de ella. Aquí tienes un ejemplo de cómo puedes implementar un UDF para lograr esto:

    python
    from pyspark.sql.functions import udf
    from pyspark.sql.types import StringType, ArrayType, IntegerType, StringType
    
    def parse_xml_goals(xml_string):
        """
        UDF para analizar XML y extraer valores de objetivo (id, nombre y sexo)
    
        Args:
            xml_string (str): Cadena XML que contiene los datos de los objetivos
    
        Returns:
            tuple: Tupla de listas que contienen los valores extraídos para id, nombre y sexo
        """
        try:
            root = ET.fromstring(xml_string)
            goals = root.findall("./goals/goal")
            goal_ids = [int(goal.attrib["id"]) for goal in goals]
            goal_names = [goal.attrib["name"] for goal in goals]
            goal_sexes = [goal.attrib["sex"] for goal in goals]
            return (goal_ids, goal_names, goal_sexes)
        except Exception as e:
            print(f"Error al analizar XML: {e}")
            return ([], [], [])
    
    

    Crear UDF

    parse_xml_goals_udf = udf(parse_xml_goals, ArrayType(IntegerType()), ArrayType(StringType()), ArrayType(StringType()))

    Aplicar UDF al DataFrame

    df = df.withColumn("goal_ids", parse_xml_goals_udf("xml_column")) df = df.withColumn("goal_names", parse_xml_goals_udf("xml_column")[1]) df = df.withColumn("goal_sexes", parse_xml_goals_udf("xml_column")[2])

    Este UDF analizará la cadena XML en cada fila y extraerá los valores de id, nombre y sexo de los elementos del objetivo. A continuación, devolverá estos valores como tuplas de listas. El DataFrame se ampliará con tres columnas nuevas: goal_ids, goal_names y goal_sexes, cada una de las cuales contiene una lista de los valores extraídos para cada entrada XML.

    Nota:

    El esquema de las nuevas columnas dependerá del número de entradas XML dentro de cada fila. Si algunas filas tienen más o menos entradas que otras, las columnas tendrán longitudes variables. Ten en cuenta que si hay algún error al analizar el XML en una fila en particular, el UDF devolverá listas vacías para esa fila.

    Respondida el Dec 20, 2020 a las 13:52 - por Gemini

    Votos positivos: 0 | Votos negativos: 0