spark estrae le colonne dalla stringa

Nov 05 2020

Hai bisogno di aiuto per analizzare una stringa, dove contiene valori per ogni attributo. di seguito è la mia stringa di esempio ...

otherPartofString Name=<Series VR> Type=<1Ac4> SqVal=<34> conn ID=<2>

a volte, la stringa può includere altri valori con un delimitatore diverso come

otherPartofString Name=<Series X> Type=<1B3> SqVal=<34> conn ID=<2> conn Loc=sfo dest=chc bridge otherpartofString.. 

le colonne di output saranno

Name      | Type | SqVal | ID | Loc  | dest 
-------------------------------------------
Series VR | 1Ac4 | 34    | 2  | null | null
Series X  | 1B3  | 34    | 2  | sfo  | chc 

Risposte

2 jxc Nov 05 2020 at 20:39

Come abbiamo discusso, per utilizzare la funzione str_to_map sui dati di esempio, possiamo impostare pairDelim e keyValueDelim nel modo seguente:

pairDelim: '(?i)>? *(?=Name|Type|SqVal|conn ID|conn Loc|dest|$)'
keyValueDelim: '=<?'

Dove pariDelim non distingue tra maiuscole e minuscole (?i)con un opzionale >seguito da zero o più SPAZI, quindi seguito da una delle chiavi predefinite (che usiamo '|'.join(keys)per generarlo dinamicamente) o dalla fine dell'ancora di stringa $. keyValueDelim è un '=' con un opzionale <.

from pyspark.sql import functions as F

df = spark.createDataFrame([                                               
   ("otherPartofString Name=<Series VR> Type=<1Ac4> SqVal=<34> conn ID=<2>",),   
   ("otherPartofString Name=<Series X> Type=<1B3> SqVal=<34> conn ID=<2> conn Loc=sfo dest=chc bridge otherpartofString..",)
],["value"])

keys = ["Name", "Type", "SqVal", "conn ID", "conn Loc", "dest"]

# add the following conf for Spark 3.0 to overcome duplicate map key ERROR
#spark.conf.set("spark.sql.mapKeyDedupPolicy", "LAST_WIN")

df.withColumn("m", F.expr("str_to_map(value, '(?i)>? *(?={}|$)', '=<?')".format('|'.join(keys)))) \
    .select([F.col('m')[k].alias(k) for k in keys]) \
    .show()
+---------+----+-----+-------+--------+--------------------+
|     Name|Type|SqVal|conn ID|conn Loc|                dest|
+---------+----+-----+-------+--------+--------------------+
|Series VR|1Ac4|   34|      2|    null|                null|
| Series X| 1B3|   34|      2|     sfo|chc bridge otherp...|
+---------+----+-----+-------+--------+--------------------+

Avremo bisogno di eseguire un po 'di post-elaborazione sui valori dell'ultima chiave mappata, poiché non ci sono ancora o pattern per distinguerli da altri testi non correlati (questo potrebbe essere un problema in quanto potrebbe accadere su qualsiasi chiave), per favore lascia so se puoi specificare qualsiasi modello.

Modifica: se l'utilizzo di map è meno efficiente per la ricerca senza distinzione tra maiuscole e minuscole poiché richiede una costosa pre-elaborazione, prova quanto segue:

ptn = '|'.join(keys)
df.select("*", *[F.regexp_extract('value', r'(?i)\b{0}=<?([^=>]+?)>? *(?={1}|$)'.format(k,ptn), 1).alias(k) for k in keys]).show()

Nel caso in cui le parentesi angolari <e >vengano utilizzate solo quando i valori o la loro successiva chiave adiacente contengono caratteri non di parole, può essere semplificato con qualche pre-elaborazione:

df.withColumn('value', F.regexp_replace('value','=(\w+)','=<$1>')) \
    .select("*", *[F.regexp_extract('value', r'(?i)\b{0}=<([^>]+)>'.format(k), 1).alias(k) for k in keys]) \
    .show()

Modifica-2: aggiunto un dizionario per gestire gli alias chiave:

keys = ["Name", "Type", "SqVal", "ID", "Loc", "dest"]

# aliases are case-insensitive and added only if exist
key_aliases = {
    'Type': [ 'ThisType', 'AnyName' ],
    'ID': ['conn ID'],
    'Loc': ['conn Loc']
}

# set up regex pattern for each key differently
key_ptns = [ (k, '|'.join([k, *key_aliases[k]]) if k in key_aliases else k) for k in keys ]  
#[('Name', 'Name'),
# ('Type', 'Type|ThisType|AnyName'),
# ('SqVal', 'SqVal'),
# ('ID', 'ID|conn ID'),
# ('Loc', 'Loc|conn Loc'),
# ('dest', 'dest')]  

df.withColumn('value', F.regexp_replace('value','=(\w+)','=<$1>')) \
    .select("*", *[F.regexp_extract('value', r'(?i)\b(?:{0})=<([^>]+)>'.format(p), 1).alias(k) for k,p in key_ptns]) \
    .show()
+--------------------+---------+----+-----+---+---+----+
|               value|     Name|Type|SqVal| ID|Loc|dest|
+--------------------+---------+----+-----+---+---+----+
|otherPartofString...|Series VR|1Ac4|   34|  2|   |    |
|otherPartofString...| Series X| 1B3|   34|  2|sfo| chc|
+--------------------+---------+----+-----+---+---+----+