val rdd = sc.parallelize(Seq(("vskp", Array(2.0, 1.0, 2.1, 5.4)),("hyd",Array(1.5, 0.5, 0.9, 3.7)),("hyd", Array(1.5, 0.5, 0.9, 3.2)),("tvm", Array(8.0, 2.9, 9.1, 2.5))))
val df1= rdd.toDF("id", "vals")
val rdd1 = sc.parallelize(Seq(("vskp","ap"),("hyd","tel"),("bglr","kkt")))
val df2 = rdd1.toDF("id", "state")
val df3 = df1.join(df2,df1("id")===df2("id"),"left")
联接操作正常 但是当我重用df2时,我面临着未解决的属性错误
val rdd2 = sc.parallelize(Seq(("vskp", "Y"),("hyd", "N"),("hyd", "N"),("tvm", "Y")))
val df4 = rdd2.toDF("id","existance")
val df5 = df4.join(df2,df4("id")===df2("id"),"left")
错误:org.apache.spark.sql.AnalysisException:已解析的属性ID#426
答案 0 :(得分:19)
正如我的评论所述,它与https://issues.apache.org/jira/browse/SPARK-10925相关,更具体地说是https://issues.apache.org/jira/browse/SPARK-14948。重复使用引用会在命名中产生歧义,因此您必须克隆df - 请参阅https://issues.apache.org/jira/browse/SPARK-14948中的最后一条注释。
答案 1 :(得分:3)
这个问题确实浪费了我很多时间,我终于找到了一个简单的解决方案。
在PySpark中,对于有问题的列,例如colA
,我们可以简单地使用
import pyspark.sql.functions as F
df = df.select(F.col("colA").alias("colA"))
在df
中使用join
之前。
我认为这也应该适用于Scala / Java Spark。
答案 2 :(得分:2)
如果您有df1和df2衍生自df1,请尝试重命名df2中的所有列,以便连接后没有两个列具有相同的名称。所以在加入之前:
所以不是df1.join(df2...
做
# Step 1 rename shared column names in df2.
df2_renamed = df2.withColumnRenamed('columna', 'column_a_renamed').withColumnRenamed('columnb', 'column_b_renamed')
# Step 2 do the join on the renamed df2 such that no two columns have same name.
df1.join(df2_renamed)
答案 3 :(得分:1)
尝试在两个连续的联接中使用一个DataFrame时遇到相同的问题。
这是问题所在:DataFrame A有2列(我们称它们为x和y),DataFrame B也有2列(我们称它们为w和z)。我需要在x = z上将A与B联接在一起,然后在y = z上将它们联接在一起。
(A join B on A.x=B.z) as C join B on C.y=B.z
我得到的确切错误是,在第二个联接中它抱怨“ 已解决的属性B.z#1234 ... ”。
在@Erik提供的链接以及一些其他博客和问题之后,我收集了一个B的副本。
这是我所做的:
val aDF = ...
val bDF = ...
val bCloned = spark.createDataFrame(bDF.rdd, bDF.schema)
aDF.join(bDF, aDF("x") === bDF("z")).join(bCloned, aDF("y") === bCloned("z"))
答案 4 :(得分:1)
在我的情况下,此错误在同一表的自我联接期间出现。 我遇到了Spark SQL而不是数据框API的以下问题:
org.apache.spark.sql.AnalysisException: Resolved attribute(s) originator#3084,program_duration#3086,originator_locale#3085 missing from program_duration#1525,guid#400,originator_locale#1524,EFFECTIVE_DATETIME_UTC#3157L,device_timezone#2366,content_rpd_id#734L,originator_sublocale#2355,program_air_datetime_utc#3155L,originator#1523,master_campaign#735,device_provider_id#2352 in operator !Deduplicate [guid#400, program_duration#3086, device_timezone#2366, originator_locale#3085, originator_sublocale#2355, master_campaign#735, EFFECTIVE_DATETIME_UTC#3157L, device_provider_id#2352, originator#3084, program_air_datetime_utc#3155L, content_rpd_id#734L]. Attribute(s) with the same name appear in the operation: originator,program_duration,originator_locale. Please check if the right attribute(s) are used.;;
我之前使用的是以下查询
SELECT * FROM DataTable as aext
INNER JOIN AnotherDataTable LAO
ON aext.device_provider_id = LAO.device_provider_id
在加入前仅选择必需的列可以为我解决问题。
SELECT * FROM (
select distinct EFFECTIVE_DATE,system,mso_Name,EFFECTIVE_DATETIME_UTC,content_rpd_id,device_provider_id
from DataTable
) as aext
INNER JOIN AnotherDataTable LAO ON aext.device_provider_id = LAO.device_provider_id
答案 5 :(得分:1)
@Json_Chans 的回答非常好,因为它不需要任何资源密集型操作。无论如何,在处理大量列时,您需要一些通用函数来动态处理这些内容,而不是手动编码数百列。
幸运的是,您可以从 Dataframe
本身派生该函数,因此除了单行代码(至少在 Python 和 pySpark 中)之外,您不需要任何其他代码:
import pyspark.sql.functions as f
df # Some Dataframe you have the "resolve(d) attribute(s)" error with
df = df.select([ f.col( column_name ).alias( column_name) for column_name in df.columns])
由于列的正确字符串表示仍存储在 Dataframe
(df.columns: list
) 的列属性中,因此您可以将其重置为自身 - 这是通过 {{1} } (注意:这仍然会产生一个新的 .alias()
,因为 Dataframe
是不可变的,这意味着它们不能被更改)。
答案 6 :(得分:0)
对于Java开发人员,请尝试调用此方法:
private static Dataset<Row> cloneDataset(Dataset<Row> ds) {
List<Column> filterColumns = new ArrayList<>();
List<String> filterColumnsNames = new ArrayList<>();
scala.collection.Iterator<StructField> it = ds.exprEnc().schema().toIterator();
while (it.hasNext()) {
String columnName = it.next().name();
filterColumns.add(ds.col(columnName));
filterColumnsNames.add(columnName);
}
ds = ds.select(JavaConversions.asScalaBuffer(filterColumns).seq()).toDF(scala.collection.JavaConverters.asScalaIteratorConverter(filterColumnsNames.iterator()).asScala().toSeq());
return ds;
}
在连接之前的两个数据集上,它将数据集克隆到新数据集中:
df1 = cloneDataset(df1);
df2 = cloneDataset(df2);
Dataset<Row> join = df1.join(df2, col("column_name"));
// if it didn't work try this
final Dataset<Row> join = cloneDataset(df1.join(df2, columns_seq));
答案 7 :(得分:0)
如果您执行以下操作,它将起作用。
假设您有一个数据框。 df1,如果您想交叉加入同一数据框,则可以使用以下
df1.toDF("ColA","ColB").as("f_df").join(df1.toDF("ColA","ColB").as("t_df"),
$"f_df.pcmdty_id" ===
$"t_df.assctd_pcmdty_id").select($"f_df.pcmdty_id",$"f_df.assctd_pcmdty_id")
答案 8 :(得分:0)
根据我的经验,我们有2个解决方案 1)克隆DF 2)在连接表之前重命名具有歧义的列。 (不要忘记删除重复的加入键)
我个人更喜欢第二种方法,因为用第一种方法克隆DF需要时间,尤其是在数据量很大的情况下。
答案 9 :(得分:0)
[TLDR]
通过将中间DataFrame写入文件系统并再次读取,来中断父DataFrame和派生DataFrame中的列之间共享的 AttributeReference 。
例如:
val df1 = spark.read.parquet("file1")
df1.createOrReplaceTempView("df1")
val df2 = spark.read.parquet("file2")
df2.createOrReplaceTempView("df2")
val df12 = spark.sql("""SELECT * FROM df1 as d1 JOIN df2 as d2 ON d1.a = d2.b""")
df12.createOrReplaceTempView("df12")
val df12_ = spark.sql(""" -- some transformation -- """)
df12_.createOrReplaceTempView("df12_")
val df3 = spark.read.parquet("file3")
df3.createOrReplaceTempView("df3")
val df123 = spark.sql("""SELECT * FROM df12_ as d12_ JOIN df3 as d3 ON d12_.a = d3.c""")
df123.createOrReplaceTempView("df123")
现在与顶级DataFrame一起加入将导致“无法解决的属性错误”
val df1231 = spark.sql("""SELECT * FROM df123 as d123 JOIN df1 as d1 ON d123.a = d1.a""")
解决方案:d123.a和d1.a共享相同的AttributeReference,将其打破 将中间表df123写入文件系统并再次读取。现在df123write.a和d1.a不共享AttributeReference
val df123 = spark.sql("""SELECT * FROM df12 as d12 JOIN df3 as d3 ON d12.a = d3.c""")
df123.createOrReplaceTempView("df123")
df123.write.parquet("df123.par")
val df123write = spark.read.parquet("df123.par")
spark.catalog.dropTempView("df123")
df123write.createOrReplaceTempView("df123")
val df1231 = spark.sql("""SELECT * FROM df123 as d123 JOIN df1 as d1 ON d123.a = d1.a""")
长话短说
我们拥有复杂的ETL,这些ETL具有在多个级别执行的DataFrame的转换和自连接。我们经常遇到“无法解析的属性”错误,我们通过选择必需的属性并在顶层表上执行联接来解决此问题,而不是直接与顶层表进行联接,这暂时解决了该问题,但是当我们对这些DataFrame应用更多转换并加入对于任何顶级DataFrame,“ unresolved attribute”错误都再次引起人们的注意。
之所以发生这种情况,是因为底层的DataFrame与来自其的顶层DataFrame共享相同的AttributeReference [more details]
因此,通过仅编写1个中间转换的DataFrame并再次读取并继续我们的ETL,我们打破了此引用共享。这中断了底部数据框和顶部数据框之间的AttributeReference共享,并且我们再也不会遇到“未解决的属性”错误。
这对我们很有用,因为当我们从顶层DataFrame转移到执行效果最差的底层数据并加入比开始的初始DataFrames收缩的数据时,由于数据量更小且火花不必回溯,它也提高了我们的性能DAG一直到最后一个持久化的DataFrame。
答案 10 :(得分:0)
只需重命名您的列并输入相同的名称。 在pyspark中: 为我在df.columns中: df = df.withColumnRenamed(i,i)
答案 11 :(得分:0)
对于scala-当我尝试使用self-join子句中的列时,出现了问题,使用方法来解决该问题
// To `and` all the column conditions
def andAll(cols: Iterable[Column]): Column =
if (cols.isEmpty) lit(true)
else cols.tail.foldLeft(cols.head) { case (soFar, curr) => soFar.and(curr) }
// To perform join different col name
def renameColAndJoin(leftDf: DataFrame, joinCols: Seq[String], joinType: String = "inner")(rightDf: DataFrame): DataFrame = {
val renamedCols: Seq[String] = joinCols.map(colName => s"${colName}_renamed")
val zippedCols: Seq[(String, String)] = joinCols.zip(renamedCols)
val renamedRightDf: DataFrame = zippedCols.foldLeft(rightDf) {
case (df, (origColName, renamedColName)) => df.withColumnRenamed(origColName, renamedColName)
}
val joinExpr: Column = andAll(zippedCols.map {
case (origCol, renamedCol) => renamedRightDf(renamedCol).equalTo(rightDf(origCol))
})
leftDf.join(renamedRightDf, joinExpr, joinType)
}
答案 12 :(得分:0)
就我而言,检查点指向原始数据框解决了该问题。