【问题标题】:__getnewargs__ error while using udf in Pyspark在 Pyspark 中使用 udf 时出现 __getnewargs__ 错误
【发布时间】:2019-10-14 09:10:24
【问题描述】:

有一个包含 2 列(db 和 tb)的数据框:db 代表数据库,tb 代表该数据库的 tableName。

   +--------------------+--------------------+
   |            database|           tableName|
   +--------------------+--------------------+
   |aaaaaaaaaaaaaaaaa...|    tttttttttttttttt|
   |bbbbbbbbbbbbbbbbb...|    rrrrrrrrrrrrrrrr|
   |aaaaaaaaaaaaaaaaa...|  ssssssssssssssssss|

我在python中有以下方法:

 def _get_tb_db(db, tb):
      df = spark.sql("select * from {}.{}".format(db, tb))
      return df.dtypes

还有这个 udf:

 test = udf(lambda db, tb: _get_tb_db(db, tb), StringType())

运行时:

 df = df.withColumn("dtype", test(col("db"), col("tb")))

有以下错误:

 pickle.PicklingError: Could not serialize object: Py4JError: An 
 error occurred while calling o58.__getnewargs__. Trace:
 py4j.Py4JException: Method __getnewargs__([]) does not exist

我发现了一些关于 stackoverflow 的讨论:Spark __getnewargs__ error 但我不知道如何解决这个问题? 错误是因为我在 UDF 中创建了另一个数据框吗?

类似于我尝试过的链接中的解决方案:

       cols = copy.deepcopy(df.columns)
       df = df.withColumn("dtype", scanning(cols[0], cols[1]))

还是报错

有什么办法吗?

【问题讨论】:

  • 你到底想做什么?
  • 我对问题添加了更多解释。
  • 一个 UDF 对每一行进行操作,并且每行返回一行。我认为这是因为df = spark.sql("select * from {}.{}".format(db, tb)) 您正在尝试使用udf 查询每行的数据库。你应该尝试map 而不是rdd
  • 谢谢,能不能多放点代码。我应该如何获得每一行的 dtypes (db.tb)?

标签: python dataframe pyspark


【解决方案1】:

错误意味着您不能在 UDF 中使用 Spark 数据帧。但是由于您包含数据库和表名称的数据框很可能很小,因此只需使用 Python for 循环就足够了,以下是一些可能有助于获取数据的方法:

from pyspark.sql import Row

# assume dfs is the df containing database names and table names
dfs.printSchema()
root
 |-- database: string (nullable = true)
 |-- tableName: string (nullable = true)

方法一:使用 df.dtypes

运行 sql select * from database.tableName limit 1 生成 df 并返回其 dtypes,将其转换为 StringType()。

data = []
DRow = Row('database', 'tableName', 'dtypes')
for row in dfs.collect():
  try:
    dtypes = spark.sql('select * from `{}`.`{}` limit 1'.format(row.database, row.tableName)).dtypes
    data.append(DRow(row.database, row.tableName, str(dtypes)))
  except Exception, e:
    print("ERROR from {}.{}: [{}]".format(row.database, row.tableName, e))
    pass

df_dtypes = spark.createDataFrame(data)
# DataFrame[database: string, tableName: string, dtypes: string]

注意:

  • 使用dtypes 而不是str(dtypes) 将得到以下架构,其中_1_2 分别是col_namecol_dtype

    root
     |-- database: string (nullable = true)
     |-- tableName: string (nullable = true)
     |-- dtypes: array (nullable = true)
     |    |-- element: struct (containsNull = true)
     |    |    |-- _1: string (nullable = true)
     |    |    |-- _2: string (nullable = true)
    
  • 使用此方法,每个表将只有一行。对于接下来的两种方法,表的每个 col_type 都有自己的行。

方法二:使用描述

您还可以通过运行spark.sql("describe tableName") 直接获取数据帧来检索此信息,然后使用reduce 函数将所有表的结果合并。

from functools import reduce

def get_df_dtypes(db, tb):
  try:
    return spark.sql('desc `{}`.`{}`'.format(db, tb)) \
                .selectExpr(
                      '"{}" as `database`'.format(db)
                    , '"{}" as `tableName`'.format(tb)
                    , 'col_name'
                    , 'data_type')
  except Exception, e:
    print("ERROR from {}.{}: [{}]".format(db, tb, e))
    pass

# an example table:
get_df_dtypes('default', 'tbl_df1').show()
+--------+---------+--------+--------------------+
|database|tableName|col_name|           data_type|
+--------+---------+--------+--------------------+
| default|  tbl_df1| array_b|array<struct<a:st...|
| default|  tbl_df1| array_d|       array<string>|
| default|  tbl_df1|struct_c|struct<a:double,b...|
+--------+---------+--------+--------------------+

# use reduce function to union all tables into one df
df_dtypes = reduce(lambda d1, d2: d1.union(d2), [ get_df_dtypes(row.database, row.tableName) for row in dfs.collect() ])

方法三:使用 spark.catalog.listColumns()

使用 spark.catalog.listColumns() 创建 collections.Column 对象列表,检索 namedataType 并合并数据。生成的数据框在它们自己的列上使用 col_name 和 col_dtype 进行规范化(与使用 Method-2 相同)。

data = []
DRow = Row('database', 'tableName', 'col_name', 'col_dtype')
for row in dfs.select('database', 'tableName').collect():
  try:
    for col in spark.catalog.listColumns(row.tableName, row.database):
      data.append(DRow(row.database, row.tableName, col.name, col.dataType))
  except Exception, e:
    print("ERROR from {}.{}: [{}]".format(row.database, row.tableName, e))
    pass

df_dtypes = spark.createDataFrame(data)
# DataFrame[database: string, tableName: string, col_name: string, col_dtype: string]

注意:不同的 Spark 发行版/版本在检索元数据时可能与 describe tbl_name 和其他命令的结果不同,请确保在查询中使用正确的列名。

【讨论】:

猜你喜欢
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 2020-03-01
  • 1970-01-01
  • 2020-07-04
  • 2018-01-18
  • 1970-01-01
相关资源
最近更新 更多