web-dev-qa-db-fra.com

Utilisation de monotonically_increasing_id () pour attribuer un numéro de ligne à pyspark dataframe

J'utilise monotonically_increasing_id () pour attribuer un numéro de ligne à pyspark dataframe en utilisant la syntaxe ci-dessous:

df1 = df1.withColumn("idx", monotonically_increasing_id())

Maintenant, df1 a 26 572 528 enregistrements. Je m'attendais donc à une valeur idx de 0 à 26 572 527.

Mais lorsque je sélectionne max (idx), sa valeur est étrangement énorme: 335,008,054,165.

Que se passe-t-il avec cette fonction? Est-il fiable d'utiliser cette fonction pour fusionner avec un autre jeu de données ayant un nombre similaire d'enregistrements?

J'ai quelque 300 images que je souhaite combiner en une seule image. Donc, une base de données contient des identifiants et d’autres contiennent différents enregistrements leur correspondant ligne par ligne.

16
muni

De la documentation

Une colonne qui génère des entiers de 64 bits à croissance monotone.

Il est garanti que l’ID généré sera en augmentation monotone et unique, mais pas consécutif. L'implémentation actuelle place l'ID de la partition dans les 31 bits supérieurs et le numéro d'enregistrement dans chaque partition dans les 33 bits inférieurs. L'hypothèse est que le cadre de données contient moins d'un milliard de partitions et que chaque partition contient moins de 8 milliards d'enregistrements.

Ainsi, ce n'est pas comme un identifiant auto-incrémenté dans les bases de données relationnelles et il n'est pas fiable pour la fusion.

Si vous avez besoin d'un comportement d'incrémentation automatique comme dans les bases de données relationnelles et que vos données peuvent être triées, vous pouvez utiliser row_number

df.createOrReplaceTempView('df')
spark.sql('select row_number() over (order by "some_column") as num, * from df')
+---+-----------+
|num|some_column|
+---+-----------+
|  1|   ....... |
|  2|   ....... |
|  3| ..........|
+---+-----------+

Si vos données ne peuvent pas être triées et que cela ne vous dérange pas d'utiliser rdds pour créer les index, puis revenir à des images de données, vous pouvez utiliser rdd.zipWithIndex()

Un exemple peut être trouvé ici

En bref:

# since you have a dataframe, use the rdd interface to create indexes with zipWithIndex()
df = df.rdd.zipWithIndex()
# return back to dataframe
df = df.toDF()

df.show()

# your data           | indexes
+---------------------+---+
|         _1          | _2| 
+-----------=---------+---+
|[data col1,data col2]|  0|
|[data col1,data col2]|  1|
|[data col1,data col2]|  2|
+---------------------+---+

Après cela, vous aurez probablement besoin de plus de transformations pour obtenir votre cadre de données tel que vous en avez besoin. Remarque: pas une solution très performante.

J'espère que cela t'aides. Bonne chance!

Edit: À bien y penser, vous pouvez combiner le monotonically_increasing_id Pour utiliser le row_number:

# create a monotonically increasing id 
df = df.withColumn("idx", monotonically_increasing_id())

# then since the id is increasing but not consecutive, it means you can sort by it, so you can use the `row_number`
df.createOrReplaceTempView('df')
new_df = spark.sql('select row_number() over (order by "idx") as num, * from df')

Pas sûr de la performance cependant.

21
mkaran

en utilisant des fonctions api que vous pouvez faire simplement comme suit

from pyspark.sql.window import Window as W
from pyspark.sql import functions as F
df1 = df1.withColumn("idx", F.monotonically_increasing_id())
windowSpec = W.orderBy("idx")
df1.withColumn("idx", F.row_number().over(windowSpec)).show()

J'espère que la réponse est utile

19
Ramesh Maharjan

J'ai trouvé la solution de @mkaran utile, mais pour moi, il n'y avait pas de colonne de commande lors de l'utilisation de la fonction window. Je voulais conserver l'ordre des lignes de dataframe sous forme d'index (ce que vous verriez dans un pandas dataframe). C'est pourquoi la solution dans la section édition a été utilisée. Comme c'est une bonne solution ( si la performance n’est pas une préoccupation), j’aimerais la partager séparément.

# Add a increasing data column 
df_index = df.withColumn("idx", monotonically_increasing_id())

# Create the window specification
w = Window.orderBy("idx")

# Use row number with the window specification
df_index = df_index.withColumn("index", F.row_number().over(w))

# Drop the created increasing data column
df2_index = df2_index.drop("idx")

df est votre image de données d'origine et df_index est le nouveau cadre de données.

2
Ankita Mehta