PySpark – Coincidencia de cadenas para crear una nueva columna

Tengo un dataframe como

ID Notes 2345 Checked by John 2398 Verified by Stacy 3983 Double Checked on 2/23/17 by Marsha 

Digamos, por ejemplo, que solo hay 3 empleados para verificar: John, Stacy o Marsha. Me gustaría hacer una nueva columna así:

 ID Notes Employee 2345 Checked by John John 2398 Verified by Stacy Stacy 3983 Double Checked on 2/23/17 by Marsha Marsha 

¿Es regex o grep mejor aquí? ¿Qué tipo de función debo probar? ¡Gracias!

EDITAR: He estado probando un montón de soluciones, pero nada parece funcionar. ¿Debo renunciar y en su lugar crear columnas para cada empleado, con un valor binario? ES DECIR:

 ID Notes John Stacy Marsha 2345 Checked by John 1 0 0 2398 Verified by Stacy 0 1 0 3983 Double Checked on 2/23/17 by Marsha 0 0 1 

En breve:

regexp_extract(col('Notes'), '(.)(by)(\s+)(\w+)', 4))

Esta expresión extrae el nombre del empleado de cualquier posición en la que se encuentra después del espacio (s) en la columna de texto ( col('Notes') )


En detalle:

Crear un dataframe de muestra

 data = [('2345', 'Checked by John'), ('2398', 'Verified by Stacy'), ('2328', 'Verified by Srinivas than some random text'), ('3983', 'Double Checked on 2/23/17 by Marsha')] df = sc.parallelize(data).toDF(['ID', 'Notes']) df.show() +----+--------------------+ | ID| Notes| +----+--------------------+ |2345| Checked by John| |2398| Verified by Stacy| |2328|Verified by Srini...| |3983|Double Checked on...| +----+--------------------+ 

Hacer las importaciones necesarias

 from pyspark.sql.functions import regexp_extract, col 

En df extraer Nombre del Employee de la columna usando regexp_extract(column_name, regex, group_number) .

Aquí regex ( '(.)(by)(\s+)(\w+)' ) significa

  • (.) – Cualquier carácter (excepto nueva línea)
  • (por) – Palabra por en el texto
  • (\ s +) – Uno o muchos espacios
  • (\ w +) – Caracteres alfanuméricos o de subrayado de longitud uno

y group_number es 4 porque group (\w+) está en la 4ª posición en la expresión

 result = df.withColumn('Employee', regexp_extract(col('Notes'), '(.)(by)(\s+)(\w+)', 4)) result.show() +----+--------------------+--------+ | ID| Notes|Employee| +----+--------------------+--------+ |2345| Checked by John| John| |2398| Verified by Stacy| Stacy| |2328|Verified by Srini...|Srinivas| |3983|Double Checked on...| Marsha| +----+--------------------+--------+ 

Cuaderno Databricks

Nota:

regexp_extract(col('Notes'), '.by\s+(\w+)', 1)) parece una versión mucho más limpia y verifique la expresión regular en uso aquí

Breve

En su forma más simple, y de acuerdo con el ejemplo proporcionado, esta respuesta debería ser suficiente, aunque el OP debe publicar más muestras si existen otras muestras donde el nombre debe ir precedido por cualquier otra palabra que no sea by .


Código

Ver código en uso aquí.

Regex

 ^(\w+)[ \t]*(.*\bby[ \t]+(\w+)[ \t]*.*)$ 

Reemplazo

 \1\t\2\t\3 

Resultados

Entrada

 2345 Checked by John 2398 Verified by Stacy 3983 Double Checked on 2/23/17 by Marsha 

Salida

 2345 Checked by John John 2398 Verified by Stacy Stacy 3983 Double Checked on 2/23/17 by Marsha Marsha 

Nota: la salida anterior separa cada columna por el carácter de tabulación \t , por lo que puede parecer que no es correcto a simple vista, pero simplemente usando un analizador de expresiones regulares en línea e insertando \t en la sección de coincidencia de expresiones regulares debe mostrarle dónde se encuentra cada columna comienza / termina


Explicación

Regex

  • ^ Afirmar posición al principio de la línea
  • (\w+) Capture uno o más caracteres de palabras ( a-zA-Z0-9_ ) en el grupo 1
  • [ \t]* Coincide con cualquier número de espacios o caracteres de tabulación ( [ \t] puede reemplazarse con \h en algunos tipos de expresiones regulares como PCRE)
  • (.*\bby[ \t]+(\w+)[ \t]*.*) Capture lo siguiente en el grupo 2
    • .* Coincidir con cualquier carácter (excepto nueva línea a menos que se use el modificador de s )
    • \bby Unir un límite de palabra \b , seguido by literalmente
    • [ \t]+ une uno o más espacios o caracteres de tabulación
    • (\w+) Capture uno o más caracteres de palabras ( a-zA-Z0-9_ ) en el grupo 3
    • [ \t]* Combina cualquier número de espacios o caracteres de tabulación
    • .* Coincidir con cualquier personaje cualquier número de veces
  • Afirmar posición al final de la línea

Reemplazo

  • \1 Coincide con el mismo texto que el último grupo de captura.
  • carácter de tabulación
  • \1 Coincide con el mismo texto que el segundo grupo de captura más reciente
  • carácter de tabulación
  • \1 Coincide con el mismo texto que el tercer grupo de captura más reciente

Algo como esto debería funcionar

 import org.apache.spark.sql.functions._ dataFrame.withColumn("Employee", substring_index(col("Notes"), "\t", 2)) 

En caso de que desee utilizar expresiones regulares para extraer el valor adecuado, necesita algo como

  dataFrame.withColumn("Employee", regexp_extract(col("Notes"), 'regex', ) 

Cuando vuelva a leer la pregunta, el OP puede hablar de una lista fija de empleados (“Digamos, por ejemplo, que solo hay 3 empleados para verificar: John, Stacy o Marsha”). Si esta es realmente una lista conocida, entonces la forma más sencilla es verificar esta lista de nombres con límites de palabras:

 regexp_extract(col('Notes'), '\b(John|Stacy|Marsha)\b', 1)