web-dev-qa-db-fra.com

Comment calculer la moyenne et l'écart type avec un PySpark DataFrame?

J'ai PySpark DataFrame (pas des pandas) appelé df qui est assez grand pour utiliser collect(). Par conséquent, le code ci-dessous n'est pas efficace. Il fonctionnait avec une plus petite quantité de données, mais maintenant il échoue.

import numpy as np

myList = df.collect()
total = []
for product,nb in myList:
    for p2,score in nb:
            total.append(score)
mean = np.mean(total)
std = np.std(total)

Existe-t-il un moyen d'obtenir mean et std comme deux variables en utilisant pyspark.sql.functions ou similaire?

from pyspark.sql.functions import mean as mean_, std as std_

Je pourrais utiliser withColumn, cependant, cette approche applique les calculs ligne par ligne et ne renvoie pas une seule variable.

MISE À JOUR:

Exemple de contenu de df:

+----------+------------------+
|product_PK|          products|
+----------+------------------+
|       680|[[691,1], [692,5]]|
|       685|[[691,2], [692,2]]|
|       684|[[691,1], [692,3]]|

Je devrais calculer la moyenne et l'écart type des valeurs de score, par exemple la valeur 1 dans [691,1] est l'un des scores.

7
Markus

Vous pouvez utiliser les fonctions intégrées pour obtenir des statistiques agrégées. Voici comment obtenir la moyenne et l'écart type.

from pyspark.sql.functions import mean as _mean, stddev as _stddev, col

df_stats = df.select(
    _mean(col('columnName')).alias('mean'),
    _stddev(col('columnName')).alias('std')
).collect()

mean = df_stats[0]['mean']
std = df_stats[0]['std']

Notez qu'il existe trois fonctions d'écart type différentes. À partir des documents, celui que j'ai utilisé (stddev) renvoie ce qui suit:

Fonction d'agrégation: renvoie l'écart type d'échantillon sans biais de l'expression dans un groupe

Vous pouvez également utiliser la méthode describe():

df.describe().show()

Reportez-vous à ce lien pour plus d'informations: pyspark.sql.functions

[~ # ~] mise à jour [~ # ~] : voici comment vous pouvez travailler sur les données imbriquées.

Utilisez explode pour extraire les valeurs dans des lignes distinctes, puis appelez mean et stddev comme indiqué ci-dessus.

Voici un MWE:

from pyspark.sql.types import IntegerType
from pyspark.sql.functions import explode, col, udf, mean as _mean, stddev as _stddev

# mock up sample dataframe
df = sqlCtx.createDataFrame(
    [(680, [[691,1], [692,5]]), (685, [[691,2], [692,2]]), (684, [[691,1], [692,3]])],
    ["product_PK", "products"]
)

# udf to get the "score" value - returns the item at index 1
get_score = udf(lambda x: x[1], IntegerType())

# explode column and get stats
df_stats = df.withColumn('exploded', explode(col('products')))\
    .withColumn('score', get_score(col('exploded')))\
    .select(
        _mean(col('score')).alias('mean'),
        _stddev(col('score')).alias('std')
    )\
    .collect()

mean = df_stats[0]['mean']
std = df_stats[0]['std']

print([mean, std])

Quelles sorties:

[2.3333333333333335, 1.505545305418162]

Vous pouvez vérifier que ces valeurs sont correctes en utilisant numpy:

vals = [1,5,2,2,1,3]
print([np.mean(vals), np.std(vals, ddof=1)])

Explication: votre colonne "products" Est un list de lists. L'appel de explode créera une nouvelle ligne pour chaque élément du list externe. Saisissez ensuite la valeur "score" De chacune des lignes éclatées, que vous avez définies comme deuxième élément d'un list à 2 éléments. Enfin, appelez les fonctions d'agrégation sur cette nouvelle colonne.

24
pault

Vous pouvez utiliser mean et stddev à partir de pyspark.sql.functions:

import pyspark.sql.functions as F

df = spark.createDataFrame(
    [(680, [[691,1], [692,5]]), (685, [[691,2], [692,2]]), (684, [[691,1], [692,3]])],
    ["product_PK", "products"]
)

result_df = (
    df
    .withColumn(
        'val_list',
        F.array(df.products.getItem(0).getItem(1),df.products.getItem(1).getItem(1))
    )
    .select(F.explode('val_list').alias('val'))
    .select(F.mean('val').alias('mean'), F.stddev('val').alias('stddev'))
)

print(result_df.collect())

qui génère:

[Row(mean=2.3333333333333335, stddev=1.505545305418162)]

Vous pouvez en savoir plus sur pyspark.sql.functions ici.

1
Mahdi

Pour l'écart-type, une meilleure façon d'écrire est la suivante. Nous pouvons utiliser la mise en forme (à 2 décimales) et en utilisant le nom de la colonne Alias

data_agg=SparkSession.builder.appName('Sales_fun').getOrCreate()    
data=data_agg.read.csv('sales_info.csv',inferSchema=True, header=True)

from pyspark.sql.functions import *

*data.select((format_number(stddev('Sales'),2)).alias('Sales_Stdev')).show()*
0
BigData-Guru