Pyspark 在另一个数据帧中查找一个数据帧中出现的所有值的最佳方法是什么?
我正在研究spark群集,我有两个数据帧。一个包含文本。另一个是查找表。这两个表都很大(M和N都很容易超过100000个条目)。匹配它们的最佳方式是什么 做交叉连接然后根据匹配过滤结果似乎是一个疯狂的想法,因为我肯定会耗尽内存 我的数据帧如下所示:Pyspark 在另一个数据帧中查找一个数据帧中出现的所有值的最佳方法是什么?,pyspark,pyspark-sql,lookup-tables,pyspark-dataframes,Pyspark,Pyspark Sql,Lookup Tables,Pyspark Dataframes,我正在研究spark群集,我有两个数据帧。一个包含文本。另一个是查找表。这两个表都很大(M和N都很容易超过100000个条目)。匹配它们的最佳方式是什么 做交叉连接然后根据匹配过滤结果似乎是一个疯狂的想法,因为我肯定会耗尽内存 我的数据帧如下所示: df1: text 0 i like apples 1 oranges are good 2 eating bananas is healthy . ... . ... M
df1:
text
0 i like apples
1 oranges are good
2 eating bananas is healthy
. ...
. ...
M tomatoes are red, bananas are yellow
df2:
fruit_lookup
0 apples
1 oranges
2 bananas
. ...
. ...
N tomatoes
output_df:
text extracted_fruits
0 i like apples ['apples']
1 oranges are good ['oranges']
2 eating bananas is healthy ['bananas']
. ...
. ...
M tomatoes are red, bananas are yellow . ['tomatoes','bananas']
我希望输出数据帧看起来像这样:
df1:
text
0 i like apples
1 oranges are good
2 eating bananas is healthy
. ...
. ...
M tomatoes are red, bananas are yellow
df2:
fruit_lookup
0 apples
1 oranges
2 bananas
. ...
. ...
N tomatoes
output_df:
text extracted_fruits
0 i like apples ['apples']
1 oranges are good ['oranges']
2 eating bananas is healthy ['bananas']
. ...
. ...
M tomatoes are red, bananas are yellow . ['tomatoes','bananas']
一种方法是使用,因为对于该型号,100K查找字应该是可管理的(默认vocabSize=262144): 基本思想是基于
df2
(查找表)中的自定义列表创建CountVectorizerModel。将df1.text
拆分为一个数组列,然后将该列转换为SparseVector,然后将其映射为单词:
编辑:在拆分函数中,将正则表达式从\s+
调整为[\s\p{Punct}]+
,以便删除所有标点符号。如果查找不区分大小写,请将'text'
更改为较低(col('text'))
from pyspark.ml.feature import CountVectorizerModel
from pyspark.sql.functions import split, udf, regexp_replace, lower
df2.show()
+---+------------+
| id|fruit_lookup|
+---+------------+
| 0| apples|
| 1| oranges|
| 2| bananas|
| 3| tomatoes|
| 4|dragon fruit|
+---+------------+
编辑-2:添加了以下df1预处理步骤,并创建一个包含所有N-gram组合的数组列。对于包含L
单词的每个字符串,N=2将在数组中添加(L-1)
更多项,如果N=3,(L-1)+(L-2)
更多项
# max number of words in a single entry of the lookup table df2
N = 2
# Pre-process the `text` field up to N-grams,
# example: ngram_str('oranges are good', 3)
# --> ['oranges', 'are', 'good', 'oranges are', 'are good', 'oranges are good']
def ngram_str(s_t_r, N):
arr = s_t_r.split()
L = len(arr)
for i in range(2,N+1):
if L - i < 0: break
arr += [ ' '.join(arr[j:j+i]) for j in range(L-i+1) ]
return arr
udf_ngram_str = udf(lambda x: ngram_str(x, N), 'array<string>')
df1_processed = df1.withColumn('words_arr', udf_ngram_str(lower(regexp_replace('text', r'[\s\p{Punct}]+', ' '))))
然后,您可以使用模型将水果映射回水果。词汇表
vocabulary = model.vocabulary
#['apples', 'oranges', 'bananas', 'tomatoes', 'dragon fruit']
to_match = udf(lambda v: [ vocabulary[i] for i in v.indices ], 'array<string>')
df_new = df3.withColumn('extracted_fruits', to_match('fruits_vec')).drop('words_arr', 'fruits_vec')
df_new.show(truncate=False)
#+----------------------------------------+----------------------+
#|text |extracted_fruits |
#+----------------------------------------+----------------------+
#|I like apples |[apples] |
#|oranges are good |[oranges] |
#|eating bananas is healthy |[bananas] |
#|tomatoes are red, bananas are yellow |[bananas, tomatoes] |
#|test |[] |
#|I have dragon fruit and apples in my bag|[apples, dragon fruit]|
#+----------------------------------------+----------------------+
其中:“\\\\b
是单词边界,以便查找值不会与上下文混淆
注意:在数据帧连接之前,您可能需要清除两列上的所有标点符号和冗余空格。spark的哪个版本?我有两个群集。一个带有spark 1.6,另一个带有spark 2.3,除非查找表中有多个单词的条目。(例如,dragon fruit).在这种情况下,您有什么建议?我是否也应该拆分查找?另一种方法是使用array_contains()连接两个数据帧或者使用rlike
和groupby+collect\u set,使用rlike
可以一次解决这个问题,尽管它可能很慢,这只是我目前的初步想法。@tooskoolforkool,如果所有条目中的最大字数都是2-3,那么可以使用类似于ngram的方法来设置words arr
列。事实上,在这个数字之上,即3,我们可以设置单独的例程来处理它们,然后将结果连接回来,我想对于超过3个单词的条目,这可能是一个更小的数据集。