将pyspark数据帧中列的字符串列表转换为用于一键编码的字符串

时间:2019-11-22 00:53:27

标签: python dataframe apache-spark pyspark

我的问题与上一个问题有关: transform columns values to columns in pyspark dataframe

我已经创建了一个表“ my_df”(pyspark中的一个数据框):

+----+--------+---------------------------------+
|id  |payment        |shop                      |
+----+--------+---------------------------------+
|dapd|[credit, cash] |[retail, on-line]         |
|wrfr|[cash, debit]  |[supermarket, brand store]|
+----+--------+---------------------------------+

现在,我需要对表进行聚类,以便可以找到“ id”的相似性。 我刚开始尝试k-均值。因此,我需要通过一键编码将分类值转换为数值。 我指的是How to handle categorical features with spark-ml?

我的代码:

from pyspark.ml import Pipeline
from pyspark.ml.feature import StringIndexer, OneHotEncoderEstimator

inputs, my_indx_list = [], []
for a_col in my_df.columns: 
  my_indx = StringIndexer(inputCol = a_col, outputCol = a_col + "_index")
  inputs.append(my_indx.getOutputCol())
  my_indx_list.append(my_indx)

  encoder = OneHotEncoderEstimator(inputCols=inputs, outputCols=[x + "_vector" for x in inputs])
  a_pipeline = Pipeline(stages = my_indx_list + [encoder])
  pipeline.fit(my_df).transform(my_df).show() # error here !

但是,我遇到了错误:

A column must be either string type or numeric type, but got ArrayType(StringType,true)

那么,我该如何解决呢?

我的想法:对每列的列表值进行排序,并将列表中的每个字符串连接为每列的长字符串。

但是,对于每一列,值都是一些调查问题的答案,并且每个答案的权重都相同。 我不确定该如何解决?

谢谢

更新

基于提出的解决方案,它可以工作,但是速度很慢。 在具有300 GB内存和32个内核的集群上,大约花费了3.5个小时。

我的代码:

   from pyspark.ml.feature import CountVectorizer
   tmp_df = original_df # 3.5 million rows and 300 columns

   for a_col in original_df.columns: 
        a_vec = CountVectorizer(inputCol = a_col, outputCol = a_col + "_index", binary=True)
        tmp_df = a_vec.fit(tmp_df).transform(tmp_df)

  tmp_df.show()

“ original_df”具有350万行和300列。

如何加快速度?

谢谢

1 个答案:

答案 0 :(得分:2)

@jxc建议在您的情况下将CountVectorizer出色地用于一键编码,该编码通常用于自然语言处理中的令牌计数。

使用CountVectorizer可以省去使用explode处理collect_setOneHotEncoderEstimator的麻烦;或更糟糕的是,如果您尝试使用udf来实现它。

给出此数据框,

df = spark.createDataFrame([
                            {'id': 'dapd', 'payment': ['credit', 'cash'], 'shop': ['retail', 'on-line']},
                            {'id': 'wrfr', 'payment': ['cash', 'debit'], 'shop': ['supermarket', 'brand store']}
                           ])
df.show()

+----+--------------+--------------------+
|  id|       payment|                shop|
+----+--------------+--------------------+
|dapd|[credit, cash]|   [retail, on-line]|
|wrfr| [cash, debit]|[supermarket, bra...|
+----+--------------+--------------------+

您可以通过在自然语言处理中将字符串数组视为标记来进行一键编码。请注意使用binary=True强制其仅返回0或1。

from pyspark.ml.feature import CountVectorizer

payment_cv = CountVectorizer(inputCol="payment", outputCol="paymentEnc", binary=True)
first_res_df = payment_cv.fit(df).transform(df)

shop_cv = CountVectorizer(inputCol="shop", outputCol="shopEnc", binary=True)
final_res_df = shop_cv.fit(first_res_df).transform(first_res_df)

final_res_df.show()

+----+--------------+--------------------+-------------------+-------------------+
|  id|       payment|                shop|         paymentEnc|            shopEnc|
+----+--------------+--------------------+-------------------+-------------------+
|dapd|[credit, cash]|   [retail, on-line]|(3,[0,2],[1.0,1.0])|(4,[0,3],[1.0,1.0])|
|wrfr| [cash, debit]|[supermarket, bra...|(3,[0,1],[1.0,1.0])|(4,[1,2],[1.0,1.0])|
+----+--------------+--------------------+-------------------+-------------------+