web-dev-qa-db-fra.com

Pyspark: PicklingError: Impossible de sérialiser l'objet:

J'ai les deux trames de données suivantes: df_whitelist et df_text

+-------+--------------------+
|keyword|    whitelist_terms |
+-------+--------------------+
|     LA|             LA city|
|     LA|        US LA in da |
| client|this client has i...|
| client|our client has do...|
+-------+--------------------+
+--------------------+----------+
|                Text|  Keywords|
+--------------------+----------+
|the client as ada...|client;ada|
|this client has l...| client;LA|
+--------------------+----------+

Dans df_whitelist, chaque mot clé correspond à un ensemble de termes, par exemple le mot-clé LA correspond à "LA city" et "US LA in da". Dans df_text, j'ai du texte et quelques mots clés trouvés dans ce texte. Ce que je veux faire, c'est que pour chaque morceau de texte, tel que "le client a ada ..", pour chacun de ses mots clés "client" et "ada", vérifiez tous les termes de la liste blanche pour ce mot-clé, pour voir comment plusieurs fois le terme est apparu dans le texte. ce que j'ai essayé est comme suit:

import pyspark.sql.functions as F
import pyspark.sql.types as T
import re
def whitelisting(text,listOfKeyword,df_whitelist):
    keywords = listOfKeyword.split(";")
    found_whiteterms_count = 0
    for k in keywords:
        if df_whitelist.filter(df_whitelist.keyword == k).count() == 0:
            found_whiteterms_count = found_whiteterms_count + 0
        else:
            df = df_whitelist.filter(df_whitelist.keyword == k).select("whitelist_terms")
            n = df.rdd.map(lambda x:len(re.findall(x["whitelist_terms"],text))).reduce(lambda x, y: x+y)
            found_whiteterms_count = found_whiteterms_count + n    
    return found_whiteterms_count     
whitelisting_udf = F.udf(lambda text,listOfKeyword: whitelisting(text,listOfKeyword,df_whitelist),T.IntegerType())
text.withColumn("whitelist_counts", whitelisting_udf(text.Text,text.Keywords))

et j'ai eu l'erreur:

PicklingError: Could not serialize object: Py4JError: An error occurred while calling o1153.__getstate__. Trace:
py4j.Py4JException: Method __getstate__([]) does not exist
    at py4j.reflection.ReflectionEngine.getMethod(ReflectionEngine.Java:318)
    at py4j.reflection.ReflectionEngine.getMethod(ReflectionEngine.Java:326)
    at py4j.Gateway.invoke(Gateway.Java:272)
    at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.Java:132)
    at py4j.commands.CallCommand.execute(CallCommand.Java:79)
    at py4j.GatewayConnection.run(GatewayConnection.Java:214)
    at Java.base/Java.lang.Thread.run(Thread.Java:844)

Je ne peux pas comprendre après avoir essayé pendant un certain temps. Quelqu'un pourrait-il bien vouloir signaler le problème et comment le résoudre? Merci.

7
user1740987

Vous passez une trame de données pyspark, df_whitelist À une UDF, les trames de données pyspark ne peuvent pas être décapées. Vous effectuez également des calculs sur une trame de données à l'intérieur d'un UDF ce qui n'est pas acceptable (pas possible). Gardez à l'esprit que votre fonction va être appelée autant de fois que le nombre de lignes de votre trame de données, vous devez donc garder les calculs simples. et ne le faites que si cela ne peut pas être fait avec les fonctions sql de pyspark.

Ce que vous devez faire à la place, c'est joindre les deux dataframes sur keyword. Commençons par les deux exemples de cadres de données que vous avez fournis:

df_whitelist = spark.createDataFrame(
    [["LA", "LA city"], ["LA", "US LA in da"], ["client", "this client has i"], ["client", "our client"]], 
    ["keyword", "whitelist_terms"])
df_text = spark.createDataFrame(
    [["the client as ada", "client;ada"], ["this client has l", "client;LA"]], 
    ["Text", "Keywords"])

La colonne Keywords dans df_text A besoin d'un traitement, nous devons transformer la chaîne en un tableau, puis l'exploser de manière à n'avoir qu'un seul élément par ligne:

import pyspark.sql.functions as F
df_text = df_text.select("Text", F.explode(F.split("Keywords", ";")).alias("keyword"))

    +-----------------+-------+
    |             Text|keyword|
    +-----------------+-------+
    |the client as ada| client|
    |the client as ada|    ada|
    |this client has l| client|
    |this client has l|     LA|
    +-----------------+-------+

Maintenant, nous pouvons joindre les deux trames de données sur keyword:

df = df_text.join(df_whitelist, "keyword", "leftouter")

    +-------+-----------------+-----------------+
    |keyword|             Text|  whitelist_terms|
    +-------+-----------------+-----------------+
    |     LA|this client has l|          LA city|
    |     LA|this client has l|      US LA in da|
    |    ada|the client as ada|             null|
    | client|the client as ada|this client has i|
    | client|the client as ada|       our client|
    | client|this client has l|this client has i|
    | client|this client has l|       our client|
    +-------+-----------------+-----------------+
  • La première condition que vous invoquez dans votre UDF peut être traduite comme suit: si keyword dans df_text N'est pas présent dans df_whitelist, Alors 0. Il équivaut à indiquant que la valeur des colonnes df_whitelist va être NULL dans le left join car elles n'apparaissent que dans le bloc de données de gauche

  • La deuxième condition: vous comptez le nombre de fois où whitelist_terms Apparaît dans Text: Text.count(whitelist_terms)

Nous allons écrire un UDF pour ce faire:

from pyspark.sql.types import IntegerType
count_terms = F.udf(lambda Text, term: Text.count(term) if term is not None else 0, IntegerType())
df = df.select(
    "Text", 
    "keyword", 
    F.when(F.isnull("whitelist_terms"), 0).otherwise(count_terms("Text", "whitelist_terms")).alias("whitelist_counts"))

    +-----------------+-------+----------------+
    |             Text|keyword|whitelist_counts|
    +-----------------+-------+----------------+
    |this client has l|     LA|               0|
    |this client has l|     LA|               0|
    |the client as ada|    ada|               0|
    |the client as ada| client|               0|
    |the client as ada| client|               0|
    |this client has l| client|               0|
    |this client has l| client|               0|
    +-----------------+-------+----------------+

Enfin, nous pouvons agréger pour revenir à une trame de données avec seulement Text distinct:

res = df.groupBy("Text").agg(
    F.collect_set("keyword").alias("Keywords"),
    F.sum("whitelist_counts").alias("whitelist_counts"))
res.show()

    +-----------------+-------------+----------------+
    |             Text|     Keywords|whitelist_counts|
    +-----------------+-------------+----------------+
    |this client has l| [client, LA]|               0|
    |the client as ada|[ada, client]|               0|
    +-----------------+-------------+----------------+
9
MaFF