web-dev-qa-db-ja.com

withColumnの下のデータフレーム列と外部リストをudfに渡す

Spark次の構造を持つデータフレームがあります。bodyText_tokenにはトークン(処理済み/単語のセット)があります。定義済みキーワードのネストされたリストがあります。

root
 |-- id: string (nullable = true)
 |-- body: string (nullable = true)
 |-- bodyText_token: array (nullable = true)

keyword_list=['union','workers','strike','pay','rally','free','immigration',],
['farmer','plants','fruits','workers'],['outside','field','party','clothes','fashions']]

各キーワードリストに含まれるトークンの数を確認し、既存のデータフレームの新しい列として結果を追加する必要がありました。例:if tokens =["become", "farmer","rally","workers","student"]結果は-> [1,2,0]になります

次の関数は期待どおりに機能しました。

def label_maker_topic(tokens,topic_words):
    twt_list = []
    for i in range(0, len(topic_words)):
        count = 0
        #print(topic_words[i])
        for tkn in tokens:
            if tkn in topic_words[i]:
                count += 1
        twt_list.append(count)

    return twt_list

WithColumnの下でudfを使用して関数にアクセスすると、エラーが発生します。外部リストをUDFに渡すことだと思います。外部リストとdatafram列をudfに渡し、新しい列をデータフレームに追加する方法はありますか?

topicWord = udf(label_maker_topic,StringType())
myDF=myDF.withColumn("topic_Word_count",topicWord(myDF.bodyText_token,keyword_list))
15
Jay

最もクリーンなソリューションは、クロージャを使用して追加の引数を渡すことです:

def make_topic_Word(topic_words):
     return udf(lambda c: label_maker_topic(c, topic_words))

df = sc.parallelize([(["union"], )]).toDF(["tokens"])

(df.withColumn("topics", make_topic_Word(keyword_list)(col("tokens")))
    .show())

これは、keyword_listまたはUDFでラップする関数の変更を必要としません。このメソッドを使用して、任意のオブジェクトを渡すこともできます。これは、たとえば、効率的な検索のためにsetsのリストを渡すために使用できます。

現在のUDFを使用してtopic_wordsを直接渡す場合は、最初にそれを列リテラルに変換する必要があります。

from pyspark.sql.functions import array, lit

ks_lit = array(*[array(*[lit(k) for k in ks]) for ks in keyword_list])
df.withColumn("ad", topicWord(col("tokens"), ks_lit)).show()

データと要件に応じて、UDF(分解+集約+折りたたみ)またはルックアップ(ハッシュ+ベクトル演算)を必要としない、より効率的な代替ソリューションを作成できます。

30
zero323

以下は、外部パラメーターをUDFに渡すことができる場合にうまく機能します(誰でも役立つように調整されたコード)

topicWord=udf(lambda tkn: label_maker_topic(tkn,topic_words),StringType())
myDF=myDF.withColumn("topic_Word_count",topicWord(myDF.bodyText_token))
8
Jay