Mar*_*usz 7 apache-spark pyspark
我在尝试根据要过滤的两列项列表从数据框中删除行时遇到了麻烦.例如,对于此数据框:
df = spark.createDataFrame([(100, 'A', 304), (200, 'B', 305), (300, 'C', 306)], ['number', 'letter', 'id'])
df.show()
+------+------+---+
|number|letter| id|
+------+------+---+
| 100| A|304|
| 200| B|305|
| 300| C|306|
+------+------+---+
Run Code Online (Sandbox Code Playgroud)
我可以使用isin一列轻松删除行:
df.where(~col('number').isin([100, 200])).show()
+------+------+---+
|number|letter| id|
+------+------+---+
| 300| C|306|
+------+------+---+
Run Code Online (Sandbox Code Playgroud)
但是当我尝试将它们删除两列时,我得到一个例外:
df.where(~array('number', 'letter').isin([(100, 'A'), (200, 'B')])).show()
Py4JJavaError: An error occurred while calling z:org.apache.spark.sql.functions.lit.
: java.lang.RuntimeException: Unsupported literal type class java.util.ArrayList [100, A]
at org.apache.spark.sql.catalyst.expressions.Literal$.apply(literals.scala:57)
at org.apache.spark.sql.functions$.lit(functions.scala:101)
at org.apache.spark.sql.functions.lit(functions.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:237)
at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
at py4j.Gateway.invoke(Gateway.java:280)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:214)
at java.lang.Thread.run(Thread.java:745)
Run Code Online (Sandbox Code Playgroud)
经过一番调查后,我意识到问题的根本原因是从非原始类型创建文字.我在pyspark中尝试了以下代码:
lit((100, 'A'))
lit([100, 'A'])
Run Code Online (Sandbox Code Playgroud)
以及scala-spark中的以下内容:
lit((100, "A"))
lit(List(100, "A"))
lit(Seq(100, "A"))
lit(Array(100, "A"))
Run Code Online (Sandbox Code Playgroud)
但没有运气......有没有人知道在spark/pyspark中创建数组文字的方法?或者是否有另一种方法可以按两列过滤数据帧?
首先你可能struct不想要arrays。请记住,Spark SQL 不支持异构数组,因此array(1, 'a')被转换为array<string>.
所以查询可能如下所示:
choices = [(100, 'A'), (200, 'B')]
target = [
struct(
lit(number).alias("number").cast("long"),
lit(letter).alias("letter").cast("string"))
for number, letter in choices]
query = struct("number", "letter").isin(target)
Run Code Online (Sandbox Code Playgroud)
这似乎生成了有效的表达式:
query
Run Code Online (Sandbox Code Playgroud)
Column<b'(named_struct(NamePlaceholder(), number, NamePlaceholder(), letter) IN (named_struct(col1, CAST(100 AS `number` AS BIGINT), col2, CAST(A AS `letter` AS STRING)), named_struct(col1, CAST(200 AS `number` AS BIGINT), col2, CAST(B AS `letter` AS STRING))))'>
Run Code Online (Sandbox Code Playgroud)
但由于某种原因分析仪失败:
df.where(~query)
Run Code Online (Sandbox Code Playgroud)
AnalysisException Traceback (most recent call last)
...
AnalysisException: "cannot resolve '(named_struct('number', `number`, 'letter', `letter`) IN (named_struct('col1', CAST(100 AS BIGINT), 'col2', CAST('A' AS STRING)), named_struct('col1', CAST(200 AS BIGINT), 'col2', CAST('B' AS STRING))))' due to data type mismatch: Arguments must be same type;;\n'Filter NOT named_struct(number, number#0L, letter, letter#1) IN (named_struct(col1, cast(100 as bigint), col2, cast(A as string)),named_struct(col1, cast(200 as bigint), col2, cast(B as string)))\n+- LogicalRDD [number#0L, letter#1, id#2L]\n"
Run Code Online (Sandbox Code Playgroud)
奇怪的是,下面的 SQL 也失败了:
df.createOrReplaceTempView("df")
spark.sql("SELECT * FROM df WHERE struct(letter, letter) IN (struct(CAST(1 AS bigint), 'a'))")
Run Code Online (Sandbox Code Playgroud)
AnalysisException: "cannot resolve '(named_struct('letter', df.`letter`, 'letter', df.`letter`) IN (named_struct('col1', CAST(1 AS BIGINT), 'col2', 'a')))' due to data type mismatch: Arguments must be same type; line 1 pos 46;\n'Project [*]\n+- 'Filter named_struct(letter, letter#1, letter, letter#1) IN (named_struct(col1, cast(1 as bigint), col2, a))\n +- SubqueryAlias df\n +- LogicalRDD [number#0L, letter#1, id#2L]\n"
Run Code Online (Sandbox Code Playgroud)
但是当两边都替换为文字时:
spark.sql("SELECT * FROM df WHERE struct(CAST(1 AS bigint), 'a') IN (struct(CAST(1 AS bigint), 'a'))")
Run Code Online (Sandbox Code Playgroud)
DataFrame[number: bigint, letter: string, id: bigint]
Run Code Online (Sandbox Code Playgroud)
工作正常,所以看起来像一个错误。
话虽如此,左反连接在这里应该可以正常工作:
from pyspark.sql.functions import broadcast
df.join(
broadcast(spark.createDataFrame(choices, ("number", "letter"))),
["number", "letter"],
"leftanti"
)
Run Code Online (Sandbox Code Playgroud)
+------+------+---+
|number|letter| id|
+------+------+---+
| 300| C|306|
+------+------+---+
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9605 次 |
| 最近记录: |