Python pyspark array_contains不区分大小写

时间:2018-01-10 06:44:03

标签: apache-spark pyspark

我有一个架构

schema = StructType([
        StructField('title', StringType(), True),
        StructField('author', ArrayType(StringType()), True),
        StructField('year', IntegerType(), True),
        StructField('url', StringType(), True)])
article = sqlContext.read.format('com.databricks.spark.xml') \
        .options(rowTag='article', excludeAttribute=True, charset='utf-8') \
        .load('source.xml', schema=schema)

其中author包含多个作者姓名。

我可以通过author过滤array_contains内的名称,如:

name = 'Tom Cat'
article.filter(array_contains(article.author, name)).show()

但是,我想知道是否有办法让我可以过滤一个忽略以下情况的名称:

name = 'tom cat'
article.filter(array_contains(article.author, name, CASE_INSENSITIVE)).show()

这样我可以得到与前一句相同的结果。

1 个答案:

答案 0 :(得分:1)

以下是两个类似的选项,它们的性能权衡不同 - 两者都应该有效,但如果性能很重要,您可能需要根据自己的数据进行衡量(如果有,请发布结果!)

选项1:自定义UDF

正如评论中所建议的,您可以编写一个UDF来将needle和haystack转换为小写(assuming text is plain ascii),例如:

import pyspark.sql.functions as func
from pyspark.sql.types import BooleanType

sc = SparkContext.getOrCreate()
sql_sc = SQLContext(sc)

df = sql_sc.createDataFrame([(["Mike", "John"], '1940'), (["Jill", "Marry"], '1950')], ("author", "year"))

def case_insensitive_array_contains(needle, haystackArr):
    lower_needle = needle.lower()
    for haystack in haystackArr:
        if lower_needle in haystack.lower():
            return True

    return False


udf_cis=func.udf(case_insensitive_search, BooleanType())


name = "John"
df.rdd.filter(lambda x: case_insensitive_array_contains(name, x['author'])).take(5)

[Row(author=['Mike', 'John'], year='1940')]

但是,与自定义UDF相关的PySpark中有一个非常重要的成本(与使用Scala的引用问题不同),因此类似的非UDF选项将是:

选项2:爆炸

(通过DF接口显示)

df1.select(['author', 'year', func.explode('author').alias('single_author')]) \
    .where(func.lower(func.col('single_author')) == name.lower()).show()

这不使用自定义UDF,但显然,如果数组往往有很多值,这可能会降低性能(当前我的本地计算机选项2运行速度更快;但对于大数据可能会有很大不同设置和/或在分布式环境中)

您也可以使用正则表达式不区分大小写的搜索而不是lower() - 我推测它会慢一些。