web-dev-qa-db-fra.com

K signifie regroupement dans PySpark

J'ai un spark dataframe 'mydataframe' avec plusieurs colonnes. J'essaie d'exécuter kmeans sur seulement deux colonnes: lat et long (latitude et longitude) en les utilisant comme valeurs simples). Je veux extraire 7 clusters sur la base de ces 2 colonnes uniquement, puis je veux attacher l'attribution du cluster à ma trame de données d'origine. J'ai essayé:

from numpy import array
from math import sqrt
from pyspark.mllib.clustering import KMeans, KMeansModel

# Prepare a data frame with just 2 columns:
data = mydataframe.select('lat', 'long')
data_rdd = data.rdd  # needs to be an RDD
data_rdd.cache()

# Build the model (cluster the data)
clusters = KMeans.train(data_rdd, 7, maxIterations=15, initializationMode="random")

Mais je reçois une erreur après un certain temps:

org.Apache.spark.SparkException: tâche abandonnée en raison d'un échec d'étape: la tâche 1 à l'étape 5191.0 a échoué 4 fois, échec le plus récent: tâche perdue 1.3 à l'étape 5191.0 (TID 260738, 10.19.211.69, exécuteur 1): org.Apache. spark.api.python.PythonException: Traceback (dernier appel le plus récent)

J'ai essayé de détacher et de rattacher le cluster. Même résultat. Qu'est-ce que je fais mal?

Merci beaucoup!

9
user3245256

Étant donné que, sur la base de ne autre question récente de votre part , je suppose que vous en êtes à vos premiers pas avec Spark clustering (vous importez même sqrt & array, sans jamais les utiliser, probablement parce que c'est comme ça dans le exemple docs ), permettez-moi d'offrir des conseils à un niveau plus général plutôt que dans la question spécifique que vous posez ici (nous espérons également vous éviter d'ouvrir par la suite 3 à 4 questions supplémentaires, en essayant de récupérer vos affectations de cluster dans votre trame de données) ...

Puisque

  1. vous avez déjà vos données dans une trame de données

  2. vous souhaitez rattacher l'appartenance au cluster à votre trame de données initiale

vous n'avez aucune raison de revenir à un RDD et d'utiliser le package MLlib ( bientôt obsolète ); vous ferez votre travail beaucoup plus facilement, avec élégance et efficacité en utilisant le package ML (maintenant recommandé), qui fonctionne directement avec les cadres de données.

Étape 0 - créez des données de jouets ressemblant aux vôtres:

spark.version
# u'2.2.0'

df = spark.createDataFrame([[0, 33.3, -17.5],
                              [1, 40.4, -20.5],
                              [2, 28., -23.9],
                              [3, 29.5, -19.0],
                              [4, 32.8, -18.84]
                             ],
                              ["other","lat", "long"])

df.show()
# +-----+----+------+
# |other| lat|  long|
# +-----+----+------+
# |    0|33.3| -17.5|
# |    1|40.4| -20.5| 
# |    2|28.0| -23.9|
# |    3|29.5| -19.0|
# |    4|32.8|-18.84|
# +-----+----+------+

Étape 1 - assemblez vos fonctionnalités

Contrairement à la plupart des packages ML, Spark ML nécessite que vos fonctionnalités d'entrée soient rassemblées dans un colonne unique de votre dataframe, généralement nommé features; et il fournit une méthode spécifique pour ce faire, VectorAssembler :

from pyspark.ml.feature import VectorAssembler

vecAssembler = VectorAssembler(inputCols=["lat", "long"], outputCol="features")
new_df = vecAssembler.transform(df)
new_df.show()
# +-----+----+------+-------------+ 
# |other| lat|  long|     features|
# +-----+----+------+-------------+
# |    0|33.3| -17.5| [33.3,-17.5]|
# |    1|40.4| -20.5| [40.4,-20.5]|
# |    2|28.0| -23.9| [28.0,-23.9]| 
# |    3|29.5| -19.0| [29.5,-19.0]|
# |    4|32.8|-18.84|[32.8,-18.84]|
# +-----+----+------+-------------+ 

Comme peut-être déjà deviné, l'argument inputCols sert à dire à VectoeAssembler quelles colonnes particulières de notre trame de données doivent être utilisées comme fonctionnalités.

Étape 2 - correspond à votre modèle KMeans

from pyspark.ml.clustering import KMeans

kmeans = KMeans(k=2, seed=1)  # 2 clusters here
model = kmeans.fit(new_df.select('features'))

select('features') sert ici à indiquer à l'algorithme la colonne de la trame de données à utiliser pour le clustering - rappelez-vous que, après l'étape 1 ci-dessus, vos fonctionnalités lat & long d'origine ne sont plus directement utilisé.

Étape 3 - transformez votre trame de données initiale pour inclure les affectations de cluster

transformed = model.transform(new_df)
transformed.show()    
# +-----+----+------+-------------+----------+ 
# |other| lat|  long|     features|prediction|
# +-----+----+------+-------------+----------+
# |    0|33.3| -17.5| [33.3,-17.5]|         0| 
# |    1|40.4| -20.5| [40.4,-20.5]|         1|
# |    2|28.0| -23.9| [28.0,-23.9]|         0|
# |    3|29.5| -19.0| [29.5,-19.0]|         0|
# |    4|32.8|-18.84|[32.8,-18.84]|         0|
# +-----+----+------+-------------+----------+

La dernière colonne de la base de données transformed, prediction, montre l'affectation du cluster - dans mon cas de jouet, j'ai fini avec 4 enregistrements dans le cluster # 0 et 1 enregistrement dans le cluster # 1.

Vous pouvez manipuler davantage la base de données transformed avec les instructions select, ou même drop la colonne features (qui a maintenant rempli sa fonction et n'est peut-être plus nécessaire). ) ...

J'espère que vous êtes beaucoup plus proche maintenant de ce que vous vouliez réellement réaliser en premier lieu. Pour extraire les statistiques de cluster, etc., ne autre réponse récente de ma part pourrait être utile ...

42
desertnaut

Malgré mon autre réponse générale, et au cas où, pour une raison quelconque, vous devriez vous en tenir à MLlib et RDD, voici ce qui provoque votre erreur en utilisant le même jouet df.

Lorsque vous select colonnes d'une trame de données à convertir en RDD, comme vous le faites, le résultat est un RDD de Lignes:

df.select('lat', 'long').rdd.collect()
# [Row(lat=33.3, long=-17.5), Row(lat=40.4, long=-20.5), Row(lat=28.0, long=-23.9), Row(lat=29.5, long=-19.0), Row(lat=32.8, long=-18.84)]

qui ne convient pas comme entrée à MLlib KMeans. Vous aurez besoin d'une opération map pour que cela fonctionne:

df.select('lat', 'long').rdd.map(lambda x: (x[0], x[1])).collect()
# [(33.3, -17.5), (40.4, -20.5), (28.0, -23.9), (29.5, -19.0), (32.8, -18.84)]

Donc, votre code devrait être comme ceci:

from pyspark.mllib.clustering import KMeans, KMeansModel

rdd = df.select('lat', 'long').rdd.map(lambda x: (x[0], x[1]))
clusters = KMeans.train(rdd, 2, maxIterations=10, initializationMode="random") # works OK
clusters.centers
# [array([ 40.4, -20.5]), array([ 30.9 , -19.81])]
4
desertnaut