Ho a che fare con Pandas DataFrame abbastanza grande - il mio set di dati assomiglia a una seguente df
configurazione:
import pandas as pd
import numpy as np
#--------------------------------------------- SIZING PARAMETERS :
R1 = 20 # .repeat( repeats = R1 )
R2 = 10 # .repeat( repeats = R2 )
R3 = 541680 # .repeat( repeats = [ R3, R4 ] )
R4 = 576720 # .repeat( repeats = [ R3, R4 ] )
T = 55920 # .tile( , T)
A1 = np.arange( 0, 2708400, 100 ) # ~ 20x re-used
A2 = np.arange( 0, 2883600, 100 ) # ~ 20x re-used
#--------------------------------------------- DataFrame GENERATION :
df = pd.DataFrame.from_dict(
{ 'measurement_id': np.repeat( [0, 1], repeats = [ R3, R4 ] ),
'time':np.concatenate( [ np.repeat( A1, repeats = R1 ),
np.repeat( A2, repeats = R1 ) ] ),
'group': np.tile( np.repeat( [0, 1], repeats = R2 ), T ),
'object': np.tile( np.arange( 0, R1 ), T )
}
)
#--------------------------------------------- DataFrame RE-PROCESSING :
df = pd.concat( [ df,
df \
.groupby( ['measurement_id', 'time', 'group'] ) \
.apply( lambda x: np.random.uniform( 0, 100, 10 ) ) \
.explode() \
.astype( 'float' ) \
.to_frame( 'var' ) \
.reset_index( drop = True )
], axis = 1
)
Nota: allo scopo di avere un esempio minimo, può essere facilmente sottoposto a sottoinsiemi (ad esempio con df.loc[df['time'] <= 400, :]
), ma poiché simulo i dati comunque, ho pensato che le dimensioni originali avrebbero dato una panoramica migliore.
Per ogni gruppo definito da ['measurement_id', 'time', 'group']
ho bisogno di chiamare la seguente funzione:
from sklearn.cluster import SpectralClustering
from pandarallel import pandarallel
def cluster( x, index ):
if len( x ) >= 2:
data = np.asarray( x )[:, np.newaxis]
clustering = SpectralClustering( n_clusters = 5,
random_state = 42
).fit( data )
return pd.Series( clustering.labels_ + 1, index = index )
else:
return pd.Series( np.nan, index = index )
Per migliorare le prestazioni ho provato due approcci:
Pacchetto Pandarallel
Il primo approccio è stato quello di parallelizzare i calcoli usando il pandarallel
pacchetto:
pandarallel.initialize( progress_bar = True )
df \
.groupby( ['measurement_id', 'time', 'group'] ) \
.parallel_apply( lambda x: cluster( x['var'], x['object'] ) )
Tuttavia, questo sembra essere non ottimale in quanto consuma molta RAM e non tutti i core vengono utilizzati nei calcoli (anche se si specifica esplicitamente il numero di core nel pandarallel.initialize()
metodo). Inoltre, a volte i calcoli vengono chiusi con vari errori, anche se non ho avuto la possibilità di trovare un motivo (forse una mancanza di RAM?).
PySpark Pandas UDF
Ho anche provato un Spark Panda UDF, anche se sono totalmente nuovo a Spark. Ecco il mio tentativo:
import findspark; findspark.init()
from pyspark.sql import SparkSession
from pyspark.conf import SparkConf
from pyspark.sql.functions import pandas_udf, PandasUDFType
from pyspark.sql.types import *
spark = SparkSession.builder.master( "local" ).appName( "test" ).config( conf = SparkConf() ).getOrCreate()
df = spark.createDataFrame( df )
@pandas_udf( StructType( [StructField( 'id', IntegerType(), True )] ), functionType = PandasUDFType.GROUPED_MAP )
def cluster( df ):
if len( df['var'] ) >= 2:
data = np.asarray( df['var'] )[:, np.newaxis]
clustering = SpectralClustering( n_clusters = 5,
random_state = 42
).fit( data )
return pd.DataFrame( clustering.labels_ + 1,
index = df['object']
)
else:
return pd.DataFrame( np.nan,
index = df['object']
)
res = df \
.groupBy( ['id_half', 'frame', 'team_id'] ) \
.apply( cluster ) \
.toPandas()
Sfortunatamente, anche le prestazioni sono state insoddisfacenti, e da quello che ho letto sull'argomento, questo potrebbe essere solo il peso dell'uso della funzione UDF, scritta in Python e la necessità associata di convertire tutti gli oggetti Python in oggetti Spark e viceversa.
Quindi, ecco le mie domande:
- Uno dei miei approcci potrebbe essere adattato per eliminare eventuali strozzature e migliorare le prestazioni? (ad es. installazione di PySpark, regolazione delle operazioni non ottimali ecc.)
- Sono alternative migliori? Come si confrontano con le soluzioni fornite in termini di prestazioni?
dask
(((quindi il mio commento è solo un consiglio per la ricerca.