我有一个架构
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()
这样我可以得到与前一句相同的结果。
答案 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() - 我推测它会慢一些。