Pyspark: как извлечь подколонки и преобразовать их в категориальные переменные - PullRequest
0 голосов
/ 05 декабря 2018

У меня проблема с искровым фреймом данных, исходящим из RandomForestRegressor, к которому мне нужно присоединиться с другим фреймом данных (исходные данные).

from pyspark import SparkContext, SparkConf
sc = SparkContext(conf=SparkConf())
from pyspark.sql import HiveContext
sqlContext = HiveContext(sc)

Вот некоторые примеры данных:

columns = ['pays', 'zol', 'group_cont_typ', 'id_periode_gestion', 'target']
vals = [('AE', 'AFRIC', 'DR', 201601, 34.67),
        ('AE', 'AFRIC', 'DR', 201602, 59.38),
        ('AE', 'ASIA', 'RF', 201601, 123.45),
        ('AE', 'ASIA', 'RF', 201602, 186.32)]

df = sqlContext.createDataFrame(vals, columns)
df.show()
+----+-----+--------------+------------------+------+
|pays|  zol|group_cont_typ|id_periode_gestion|target|
+----+-----+--------------+------------------+------+
|  AE|AFRIC|            DR|            201601| 34.67|
|  AE|AFRIC|            DR|            201602| 59.38|
|  AE| ASIA|            RF|            201601|123.45|
|  AE| ASIA|            RF|            201602|186.32|
+----+-----+--------------+------------------+------+

Я преобразовал 3 категориальные переменные в числовые, чтобы использовать их в прогнозе:

si_pays = StringIndexer(inputCol='pays', outputCol='pays_encode')
si_zol = StringIndexer(inputCol='zol', outputCol='zol_encode')
si_type = StringIndexer(inputCol='group_cont_typ', outputCol='group_cont_typ_encode')
df = si_pays.fit(df).transform(df)
df = si_zol.fit(df).transform(df)
df = si_type.fit(df).transform(df)

Затем я изменил формат кадра данных, чтобы он соответствовал требуемому формату для RandomForestRegressor:

input_cols = ['pays_encode', 'zol_encode', 'group_cont_typ_encode', 'id_periode_gestion']
df = df.rdd.map(lambda x: (x['target'], 
                               Vectors.dense([x[col] for col in input_cols]))) \
        .toDF(["label", "features"]) \
        .select([F.col('label').cast(DoubleType()).alias('label'), 'features'])

Это позволяет мне запускать прогнозы (я обычно тренируюсь и тестирую на разных подмножествах, но для простоты я делаю все это на одном и том же кадре данных):

from pyspark.ml.regression import RandomForestRegressor
rf = RandomForestRegressor(numTrees=120, maxDepth=8, maxBins=64)
model = rf.fit(df)
forecasts = model.transform(df)
forecasts.show()
+------+--------------------+-----------------+
| label|            features|       prediction|
+------+--------------------+-----------------+
| 34.67|[57.0,0.0,0.0,201...|38.58532881795905|
| 59.38|[57.0,0.0,0.0,201...|69.21916671695188|
|123.45|[57.0,8.0,1.0,201...|94.17987290587061|
|186.32|[57.0,8.0,1.0,201...| 91.3936760453811|
+------+--------------------+-----------------+

Теперь, когда мы дошли до этой точки, вот моя проблема : Мне нужно объединить результаты с исходными данными, чтобы представить их, и способ объединить их - это столбцы, которыебыли преобразованы .


Итак, я извлекаю подколонки функций:

from pyspark.sql.functions import udf, col

def to_array(col):
    def to_array_(v):
        return v.toArray().tolist()
    return udf(to_array_, ArrayType(DoubleType()))(col)

forecasts = forecasts.withColumn("feature", to_array(col("features"))).select(["prediction", "label"] +
                                                                                  [col("feature")[i] for i in range(4)])

Затем я хочу вернуть переменные к их исходным категориямзначения с IndexToString, но это не такрк.

back_to_pays = IndexToString().setInputCol("feature[0]").setOutputCol("pays")
back_to_zol = IndexToString().setInputCol("feature[1]").setOutputCol("zol")
back_to_type = IndexToString().setInputCol("feature[2]").setOutputCol("group_cont_typ")
forecasts = back_to_pays.transform(forecasts)
forecasts = back_to_zol.transform(forecasts)
forecasts = back_to_type.transform(forecasts)
forecasts.show()


Traceback (most recent call last):
"/runner.py", line 109, in _run_spark
    forecasts = back_to_pays.transform(forecasts)
File "/usr/hdp/2.6.2.0-205/spark/python/lib/pyspark.zip/pyspark/ml/pipeline.py", line 114, in transform
File "/usr/hdp/2.6.2.0-205/spark/python/lib/pyspark.zip/pyspark/ml/wrapper.py", line 149, in _transform
File "/usr/hdp/2.6.2.0-205/spark/python/lib/py4j-0.9-src.zip/py4j/java_gateway.py", line 813, in __call__
File "/usr/hdp/2.6.2.0-205/spark/python/lib/pyspark.zip/pyspark/sql/utils.py", line 45, in deco
File "/usr/hdp/2.6.2.0-205/spark/python/lib/py4j-0.9-src.zip/py4j/protocol.py", line 308, in get_return_value
py4j.protocol.Py4JJavaError: An error occurred while calling o354.transform.
: java.lang.ClassCastException: org.apache.spark.ml.attribute.UnresolvedAttribute$ cannot be cast to org.apache.spark.ml.attribute.NominalAttribute
    at org.apache.spark.ml.feature.IndexToString.transform(StringIndexer.scala:292)
    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:231)
    at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:381)
    at py4j.Gateway.invoke(Gateway.java:259)
    at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:133)
    at py4j.commands.CallCommand.execute(CallCommand.java:79)
    at py4j.GatewayConnection.run(GatewayConnection.java:209)
    at java.lang.Thread.run(Thread.java:745)

Как мне перейти к преобразованию в категориальные данные при извлечении подколонок из векторного столбца?

1 Ответ

0 голосов
/ 05 декабря 2018

Было бы проще присоединиться, если бы существовал столбец id, но вы можете создать, используя номер строки, а затем объединить их, используя номер строки, поскольку позиции строк не изменяются во время преобразований.

from pyspark.sql.window import Window
from pyspark.sql.functions import rowNumber

w = Window().orderBy()

df =  df.withColumn("rowid", rowNumber().over(w))
forecasts =  forecasts.withColumn("rowid", rowNumber().over(w))

mergedDF = df.join(forecasts, "rowid").drop("rowid")
mergedDF.show()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...