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.