Pandas 그룹화 된 DataFrame에 Python 함수 적용-계산 속도를 높이는 가장 효율적인 방법은 무엇입니까?


9

나는 매우 큰 Pandas DataFrame을 다루고 있습니다. 내 데이터 세트는 다음 df설정 과 유사합니다 .

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
                )

참고 : 최소한의 예를 들기 위해 쉽게 서브 세트를 사용할 수 있지만 (예 :) df.loc[df['time'] <= 400, :]어쨌든 데이터를 시뮬레이션하기 때문에 원래 크기가 더 나은 개요를 제공한다고 생각했습니다.

에 의해 정의 된 각 그룹에 ['measurement_id', 'time', 'group']대해 다음 함수를 호출해야합니다.

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 )

성능을 향상시키기 위해 두 가지 접근 방식을 시도했습니다.

판다 렐렐 패키지

첫 번째 방법은 pandarallel패키지를 사용하여 계산을 병렬화하는 것이 었습니다 .

pandarallel.initialize( progress_bar = True )
df \
  .groupby( ['measurement_id', 'time', 'group'] ) \
  .parallel_apply( lambda x: cluster( x['var'], x['object'] ) )

그러나 이것은 많은 RAM을 소비하고 계산에 모든 코어가 사용되는 것은 아니기 때문에 (최소한 pandarallel.initialize()방법 으로 코어 수를 지정하더라도) 차선책으로 보입니다 . 또한 때로는 여러 가지 오류로 계산이 종료되지만 그 이유를 찾을 수있는 기회는 없었지만 (RAM이 부족할 수 있습니까?)

PySpark Pandas UDF

Spark에 완전히 익숙하지는 않지만 Spark Pandas UDF도 사용했습니다. 내 시도는 다음과 같습니다.

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()

불행히도 성능도 만족스럽지 못했고 주제에서 읽은 내용은 Python으로 작성된 UDF 함수를 사용하고 모든 Python 객체를 Spark 객체로 변환 해야하는 부담이있을 수 있습니다.

내 질문은 다음과 같습니다.

  1. 병목 현상을 제거하고 성능을 개선하기 위해 내 접근 방식 중 하나를 조정할 수 있습니까? (예 : PySpark 설정, 최적이 아닌 작업 조정 등)
  2. 더 나은 대안이 있습니까? 성능 측면에서 제공된 솔루션과 어떻게 비교됩니까?

2
당신은 황혼 을 연구 했습니까?
— Danila Ganchar

1
아직,하지만 덕분에 당신의 제안에 대해 - 나는 한번 풀어주지
— Kuba_

불행히도 나는 dask((내 의견은 연구를위한 조언
— 일뿐

성능상 나는 계산이 완료 될 수있는 시간을 의미했습니다.
— Kuba_

답변:


1

Q는 : " 수 중 내 방식의 조정 성능 병목을 제거하고 개선하기 위해? (예를 들어 PySpark 설정 등 하위 최적의 작업을 조정) "

+1컴퓨팅 전략 중 하나 에 대한 설정 애드온 오버 헤드 비용 을 언급했습니다 . 이것은 항상 손익 분기점을 유지하며, 그 후에야 비 [SERIAL]전략이 원하는 [TIME]도메인 속도 향상에 유리한 기쁨을 얻을 수 있습니다 (그렇지만 일반적으로 [SPACE]도메인 비용이 허용되거나 유지 가능합니다-RAM). .. 그러한 크기의 장치, 예산 및 기타 유사한 실제 제약 조건의 존재 및 액세스)

첫째,
이륙 전 비행 전 점검
Amdahl의 법칙
의 새로운 오버 헤드-엄격한 공식화 는 현재 이러한 추가 pSO + pTO오버 헤드를 모두 통합 할 수 있으며이를 손익분기 점을 포함한 달성 가능한 스피드 업 레벨 예측에 반영합니다. 그 점이 의미가 있기 때문에 (비용 / 효과, 효율성 측면에서) 병행하는 것이 의미가있을 수 있습니다.

여기에 이미지 설명을 입력하십시오

그러나
이것이 우리의 핵심 문제 는 아닙니다 .
이것은 다음에 온다 :

다음
의 계산 비용 주어진 SpectralClustering()방사형 볼츠만 기능 커널을 사용하려면 여기를 것입니다, ~ exp( -gamma * distance( data, data )**2 )의 분할로부터 사전 없을 것 같다 data는 AS, 분리 된 업무 단위의 수에 -object를 distance( data, data )정의, -component, 가지고 있지만에 모든 data요소를 방문하십시오 (모든 값을 전달하는 { process | node }분산 토폴로지 의 통신 비용은 명백한 이유로 인해 { process | node }분산 처리 의 최악의 사용 사례가 아니라면 끔찍하게 나빠집니다. (실제로 비전적이고 메모리가 적거나 상태가 적지 만 컴퓨팅 패브릭을 제외하고).

현학적 인 분석을 위해, 예 -이 추가 (우리는 이미 말할 수 있습니다 나쁜 -의 비용 상태를) 다시 - 모든 -에 - 어떤 K-수단이 여기에 대해,하는 과정과 O( N^( 1 + 5 * 5 ) )위해, 간다 N ~ len( data ) ~ 1.12E6+, 지독하게 우리의 소원에 대해 몇 가지를 가지고 똑똑하고 빠른 처리.

그래서 무엇?

설치 비용은 무시하지 않는 동안, 증가 통신 비용은 거의 위의 스케치 시도를 사용에서 확인 비활성화 어떤 개선을위한 pure-에서 이동하는 것입니다 [SERIAL]어떤 형태로 프로세스 흐름 단지 - [CONCURRENT]또는 True- [PARALLEL]일부 작업 하위 단위의 오케스트레이션 , 모든 값 전달 토폴로지 를 구현해야하는 직렬 연결 과 관련된 오버 헤드가 증가했기 때문 입니다.

그게 아니었다면?

글쎄, 이것은 컴퓨팅 과학 [TIME]옥시 모론처럼 들립니다. 가능하더라도 아무리 많은 사전 계산 된 거리의 비용 (도메인 복잡성 비용이 "사전"이 필요합니다 (어떻게? 어떻게?) 피할 수없는 다른 대기 시간으로, 미래까지의 모든 거리 매트릭스의 일부 (아직까지 알려지지 않은) 증분 빌드에 의해 가능한 대기 시간 마스킹이 가능합니까?)) [TIME]-및- [SPACE]도메인.

Q : " 더 나은 대안이 있습니까? "

단 하나, 내가 지금까지 알고 오프 오전, 문제가하는 QUBO 공식화, 문제 패션 (참고 : 다른에 재 공식화 얻을 수 있습니다한다면, 시도하는 것입니다 Q uantum- U nconstrained- B inary- O ptimisation 좋은 소식은이를위한 도구, 직접적인 지식과 실질적인 문제 해결 경험의 기초가 존재하고 더 커진다는 것입니다

Q : 성능 측면 에서 제공된 솔루션과 어떻게 비교 됩니까?

QUBO 공식 문제에는 O(1)일정 시간 ( [TIME]도메인 내) 에 유망한 (!) 솔버 가 있으며-도메인 에서는 다소 제한적입니다 [SPACE](최근에 발표 된 LLNL 트릭은이 물리적 세계, 현재 QPU 구현, 문제 제한을 피하는 데 도움이 될 수 있음) 크기).


이것은 흥미로운 답변이지만 요점을 놓치는 것 같습니다. OP는 단일 모델이 아닌 여러 개의 작은 모델을 훈련시킵니다. 따라서 핵심 관찰은 대부분 관련이 없습니다.
— user10938362

@ user10938362 소유권이 주장 된 부동산 ( 작은 모델 훈련 )이 위에 언급 된 처리 비용에 대한 대규모 통계를 어떻게 해석합니까? 물론 많은 작은 모델의 이론적 단지 선형 적으로 증가 합 약속 (여전히) 개인의 큰-O 비용 (N 지금 작은, 그러나 다른 요인에서) 처리를, 그러나,이 모든 몹시 더 비싼 금액에 추가해야 설정 및 종료 오버 헤드 비용의 추가 비용과 모든 추가 통신 오버 헤드 비용 (모수 / 데이터 / 결과 + 일반적으로 각 단계에서 SER / DES 처리 비용 쌍)
— user3666197

0

이것은 대답이 아니지만 ...

당신이 실행하는 경우

df.groupby(['measurement_id', 'time', 'group']).apply(
    lambda x: cluster(x['var'], x['object']))

(즉, Pandas 만 사용하면) 이미 여러 코어를 사용하고 있음을 알 수 있습니다. 기본적으로 작업을 병렬화 하기 sklearn위해 사용 하기 때문 joblib입니다. 당신은 할 수 DASK에 찬성 스케줄러를 교환 아마도 스레드 간의 데이터 공유를 통해 더 효율을 얻을 수 있지만, 그래서 당신이하고있는 작업과 같은 CPU 바인딩과 같이, 당신은 그것을 속도를 높이기 위해 할 수있는 일은 없을 것입니다.

간단히 말해 이것은 알고리즘 문제입니다. 컴퓨팅을위한 다른 프레임 워크를 고려하기 전에 실제로 계산해야하는 항목을 파악하십시오.


일단 작업 분할이 일단 생성 된 프로세스에 의해 구성되고 스레드와는 관련이없고 공유와는 관련이없는 "… 스레드 간 데이터 공유…"에 대해 왜 설명해 주 시겠습니까? 논쟁에 대한 당신의 친절한 설명에 감사드립니다. joblib
— user3666197

정확히, jboblib는 일반적으로 프로세스를 사용하지만 스레드와 프로세스의 혼합을 선택할 수있는 백엔드로 dask를 사용할 수도 있습니다.
— mdurant

나는 병렬 컴퓨팅에 익숙하지 않지만 sklearn이 병렬화를 사용 하더라도이 설정에서 쓸모가 없습니까? sklearn이 수행하는 작업은 각 클러스터링 작업이 10 포인트에만 적용되므로 매우 간단합니다. 다시 말하지만, 여기에 잘못되었을 수도 있지만 원래 데이터 덩어리를 병렬 처리하는 방법은 실제 문제라고 생각합니다.
— Kuba_

"이 설정에서는 쓸모가 없습니다"– 글쎄, 당신은 1 대신 8 개의 CPU 코어 성능을 사용합니다.
— mdurant

0

에 대한 전문가는 Dask아니지만 다음 코드를 기준으로 제공합니다.

import dask.dataframe as ddf

df = ddf.from_pandas(df, npartitions=4) # My PC has 4 cores

task = df.groupby(["measurement_id", "time", "group"]).apply(
    lambda x: cluster(x["var"], x["object"]),
    meta=pd.Series(np.nan, index=pd.Series([0, 1, 1, 1])),
)

res = task.compute()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.