使用PySpark进行高效的文本预处理(清除,标记化,停用词,词干,过滤器)

时间:2018-12-02 10:40:21

标签: python apache-spark pyspark apache-spark-sql text-processing

最近,我开始在《学习火花》一书中学习火花。从理论上讲,一切都清楚,在实践中,我面对的事实是,我首先需要对文本进行预处理,但是关于此主题没有实际的提示。

首先要考虑的是,现在最好使用数据框而不是RDD,因此我对数据框进行了预处理。

所需操作:

  1. 从标点中清除文本(regexp_replace)
  2. 令牌化(令牌生成器)
  3. 删除停用词(StopWordsRemover)
  4. 气化(SnowballStemmer)
  5. 过滤短词(udf)

我的代码是:

from pyspark.sql import SparkSession
from pyspark.sql.functions import udf, col, lower, regexp_replace
from pyspark.ml.feature import Tokenizer, StopWordsRemover
from nltk.stem.snowball import SnowballStemmer

spark = SparkSession.builder \
    .config("spark.executor.memory", "3g") \
    .config("spark.driver.cores", "4") \
    .getOrCreate()
df = spark.read.json('datasets/entitiesFull/full').select('id', 'text')

# Clean text
df_clean = df.select('id', (lower(regexp_replace('text', "[^a-zA-Z\\s]", "")).alias('text')))

# Tokenize text
tokenizer = Tokenizer(inputCol='text', outputCol='words_token')
df_words_token = tokenizer.transform(df_clean).select('id', 'words_token')

# Remove stop words
remover = StopWordsRemover(inputCol='words_token', outputCol='words_clean')
df_words_no_stopw = remover.transform(df_words_token).select('id', 'words_clean')

# Stem text
stemmer = SnowballStemmer(language='english')
stemmer_udf = udf(lambda tokens: [stemmer.stem(token) for token in tokens], ArrayType(StringType()))
df_stemmed = df_words_no_stopw.withColumn("words_stemmed", stemmer_udf("words_clean")).select('id', 'words_stemmed')

# Filter length word > 3
filter_length_udf = udf(lambda row: [x for x in row if len(x) >= 3], ArrayType(StringType()))
df_final_words = df_stemmed.withColumn('words', filter_length_udf(col('words_stemmed')))

处理需要很长时间,整个文档的大小为60 GB。使用RDD是否有意义?缓存会有所帮助吗?如何优化预处理?

首先,我在本地计算机上测试了实现,然后将在集群上进行尝试。本地计算机-Ubuntu RAM 6Gb,4个CPU。也欢迎任何替代解决方案。谢谢!

1 个答案:

答案 0 :(得分:0)

JSON通常是Spark分析最糟糕的文件格式,尤其是如果它是单个60GB JSON文件。 Spark适用于1GB Parquet文件。进行一些预处理会很有帮助:

temp_df = spark.read.json('datasets/entitiesFull/full').select('id', 'text').repartition(60)
temp_df.write.parquet('some/other/path')
df = spark.read.parquet('some/other/path')
# ... continue the rest of the analysis

从性能的角度来看,在UDF中包装SnowballStemmer并不是最好的方法,但是除非您能以低级Java字节码编写算法,否则这是最现实的方法。我也使用UDF在ceja中创建了Porter Stemming算法。

这里是an example of a native implementation of a Spark function。可以实现,但并不容易。