Pandas DataFrame에서 다중 처리 사용

Mehvish Ashiq 2024년2월15일
  1. 다중 처리 소개
  2. 다중 처리 사용의 중요성
  3. Pandas DataFrame에서 다중 처리 사용
Pandas DataFrame에서 다중 처리 사용

이 자습서에서는 Python의 다중 처리를 소개하고 코드 예제와 그래픽 표현을 사용하여 교육합니다. 또한 다중 처리의 중요성을 강조하고 Pandas 데이터 프레임과 함께 다중 처리 모듈을 사용하는 방법을 보여줍니다.

다중 처리 소개

멀티프로세싱이란 시스템이 동시에 여러 프로세서를 지원할 수 있는 기능을 갖는 것을 의미합니다. 다중 처리에서 응용 프로그램은 독립적으로 또는 덜 독립적으로 실행될 수 있는 더 작은 루틴으로 분할됩니다.

운영 체제는 이러한 스레드를 다른 프로세서에 할당하여 시스템 성능을 향상시킵니다. 어떻게? 다음의 간단한 프로그램을 통해 이해해 보자.

예제 코드:

import time

start = time.perf_counter()


def print_something():
    print("Sleeping for 1 second.")
    time.sleep(1)
    print("Done with sleeping.")


print_something()
finish = time.perf_counter()
print(f"Completed in {round(finish-start,2)} second(s)")

출력:

Sleeping for 1 second.
Done with sleeping.
Completed in 1.01 second(s)

코드 펜스에서 스크립트 실행에 걸리는 시간을 측정하는 데 사용할 time 모듈을 먼저 가져왔기 때문에 위의 출력이 올바르게 들립니다. 이를 측정하기 위해 start = time.perf_counter()finish = time.perf_counter()를 사용하여 시작종료 시간을 계산했습니다.

우리는 또한 무엇인가를 인쇄하고, 1초 동안 잠자고, 또 다른 문장을 인쇄하는 print_something()이라는 함수를 가지고 있습니다. 우리는 이 함수를 호출하고 마지막으로 스크립트를 완성했음을 보여주는 마지막 문장을 인쇄합니다.

이제 print_something()을 두 번 실행하면 거의 2초가 걸립니다. 아래에서 확인할 수 있습니다.

예제 코드:

import time

start = time.perf_counter()


def print_something():
    print("Sleeping for 1 second.")
    time.sleep(1)
    print("Done with sleeping.")


print_something()
print_something()

finish = time.perf_counter()
print(f"Completed in {round(finish-start,2)} second(s)")

출력:

Sleeping for 1 second.
Done with sleeping.
Sleeping for 1 second.
Done with sleeping.
Completed in 2.02 second(s)

이제 프로그램은 1초 동안 두 번 잠자기 때문에 스크립트를 완료하는 데 거의 2초가 걸립니다.

따라서 이 print_something() 함수를 실행할 때마다 스크립트에 약 1초가 추가되는 것을 볼 수 있습니다. 우리의 스크립트는 1초 동안 대기하다가 다음 함수로 이동하고 또 다른 1초를 기다립니다.

그런 다음 그 시점에서 작업이 완료되고 스크립트가 완료됩니다. 다음 그래픽 표현을 통해 이해할 수 있습니다.

python은 pandas 데이터 프레임에서 다중 처리 사용 - 그래픽 표현 1

이 그래픽 표현은 우리가 함수(이 경우 print_something())를 실행하고, 1초 동안 기다렸다가 함수를 다시 실행한 다음 또 다른 초를 기다리고 완료되면 최종 print를 인쇄함을 보여줍니다. 우리가 끝났다는 것을 보여주는 진술.

위의 그래픽 표현에서 볼 수 있듯이 이 순서로 스크립트를 실행하는 것을 동기식 실행이라고 합니다.

이제 동기식으로 실행할 필요가 없는 작업이 있는 경우 multiprocessing 모듈을 사용하여 이러한 작업을 다른 CPU로 분할하고 동시에 실행할 수 있습니다.

멀티스레딩멀티프로세싱과 같지 않습니다. 여기에서 차이점을 찾을 수 있습니다.

multiprocessing 모듈을 사용하려는 경우 그래픽 표현은 다음과 같습니다.

python은 pandas 데이터 프레임에서 다중 처리 사용 - 그래픽 표현 2

여기에는 여전히 두 가지 작업이 있지만 이를 서로 다른 프로세스에서 동시에 실행되는 두 개의 프로세스로 나눕니다. 이제 다음과 같이 프로그램에서 이 그래픽 표현을 구현해 보겠습니다.

import multiprocessing
import time


start = time.perf_counter()


def print_something():
    print("Sleeping for 1 second.")
    time.sleep(1)
    print("Done with sleeping.")


process1 = multiprocessing.Process(target=print_something)
process2 = multiprocessing.Process(target=print_something)

process1.start()
process2.start()

process1.join()
process2.join()

finish = time.perf_counter()
print(f"Completed in {round(finish-start,2)} second(s)")

출력:

Sleeping for 1 second.
Done with sleeping.
Sleeping for 1 second.
Done with sleeping.
Completed in 1.02 second(s)

이제 볼 수 있듯이 스크립트는 2초가 아닌 1초가 걸립니다. 위의 스크립트는 몇 가지 수정 사항을 제외하고 이전 스크립트와 유사합니다.

프로세스 생성에 사용할 multiprocessing 모듈을 가져왔습니다. 그런 다음 multiprocessing.Process(target=print_something)을 사용하여 두 개의 프로세스를 만들고 process1process2에 저장합니다.

그런 다음 process1.start()process2.start()를 사용하여 두 프로세스를 모두 시작합니다. 우리는 .join() 메서드를 사용하여 프로세스를 완료하기 전에 나머지 스크립트를 실행하지 않도록 했습니다.

즉, .join() 메소드를 생략하면 프로세스가 완료되기 전에 다음 두 명령문이 실행됩니다.

finish = time.perf_counter()
print(f"Completed in {round(finish-start,2)} second(s)")

다음 예제 코드를 사용하여 배울 수 있습니다.

import multiprocessing
import time


start = time.perf_counter()


def print_something():
    print("Sleeping for 1 second.")
    time.sleep(1)
    print("Done with sleeping.")


process1 = multiprocessing.Process(target=print_something)
process2 = multiprocessing.Process(target=print_something)

process1.start()
process2.start()

# process1.join()
# process2.join()

finish = time.perf_counter()
print(f"Completed in {round(finish-start,2)} second(s)")

출력:

Completed in 0.01 second(s)
Sleeping for 1 second.
Done with sleeping.
Sleeping for 1 second.
Done with sleeping.

보시다시피 SleepingDone 문 앞에 Completed 문을 인쇄합니다. 이를 방지하기 위해 .join() 메서드를 사용할 수 있습니다.

다중 처리 사용의 중요성

하나의 프로세서가 있는 기계(PC/노트북)가 있다고 가정합니다. 여러 프로세스를 동시에 할당하면 모든 프로세스를 계속 실행하기 위해 모든 작업을 방해하거나 중단하고 한 프로세스에서 다른 프로세스로 전환해야 합니다.

여기에서 멀티프로세싱의 개념이 등장합니다. 멀티프로세싱 컴퓨터는 여러 개의 중앙 프로세서(멀티프로세서) 또는 코어(멀티코어 프로세서)라고 하는 두 개 이상의 독립적인 실제 처리 장치가 있는 하나의 컴퓨팅 구성 요소를 가질 수 있습니다.

여기에서 CPU는 여러 작업을 동시에 쉽게 실행하며 각 작업은 해당 프로세스를 사용합니다. 이런 식으로 프로그램 속도를 높이고 많은 시간과 비용을 절약할 수 있습니다.

Pandas DataFrame에서 다중 처리 사용

우리는 multiprocessing 모듈, 기본 사용법 및 중요성에 대한 충분한 지식을 가지고 있습니다. 이 모듈을 데이터 프레임과 함께 사용하는 방법을 알아보겠습니다.

  • 라이브러리 및 모듈을 가져옵니다.

    먼저 필요한 모든 모듈과 라이브러리를 가져옵니다.

    import pandas as pd
    from geopy.distance import geodesic
    from itertools import combinations
    import multiprocessing as mp
    
  • 데이터 프레임을 만듭니다.

    serial_number, column_name, latlon 열과 해당 값을 포함하는 데이터 프레임을 생성합니다.

    df = pd.DataFrame(
        {
            "serial_number": [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
            "column_name": ["aa", "aa", "aa", "bb", "bb", "bb", "bb", "cc", "cc", "cc"],
            "lat": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
            "lon": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
        }
    )
    
  • 작업을 분할합니다.

    프로세스 간에 작업을 분할하여 아마도 모든 튜플 (grp, lst)를 별도의 프로세스로 보냅니다. 다음 코드 행은 동일한 작업을 수행합니다.

    grp_lst_args = list(df.groupby("column_name").groups.items())
    print(grp_lst_args)
    # [('aa', [0, 1, 2]), ('cc', [7, 8, 9]), ('bb', [3, 4, 5, 6])]
    

    그런 다음 각 튜플을 별도의 프로세스에서 함수(이 경우 calc_dist())에 대한 인수로 보냅니다. 아래에서 살펴보겠습니다.

  • 튜플 목록을 함수에 보냅니다.

    calc_dist()list 유형 매개변수를 사용하여 거리를 계산하고 데이터 프레임으로 반환합니다. pd.DataFrame()은 데이터 프레임으로 변환하는 데 사용됩니다.

    여기서 목록에는 [('aa', [0, 1, 2]), ('cc', [7, 8, 9]), ('bb', [3, 4, 5, 6] 세 개의 튜플이 포함됩니다. ])] 이전에 생성했습니다.

    def calc_dist(arg):
        grp, lst = arg
        return pd.DataFrame(
            [
                [
                    grp,
                    df.loc[c[0]].serial_number,
                    df.loc[c[1]].serial_number,
                    geodesic(df.loc[c[0], ["lat", "lon"]], df.loc[c[1], ["lat", "lon"]]),
                ]
                for c in combinations(lst, 2)
            ],
            columns=["column_name", "machine_A", "machine_B", "Distance"],
        )
    
  • 다중 처리를 구현합니다.

    이 시점에서 다중 처리를 사용하여 각 튜플에 대해 calc_dist() 함수를 동시에 호출합니다. 다음 코드에서는 각 튜플에 대해 calc_dist() 함수를 병렬로 실행하기 위해 Pool()을 사용합니다.

    pool = mp.Pool(processes=(mp.cpu_count() - 1))
    results = pool.map(calc_dist, grp_lst_args)
    pool.close()
    pool.join()
    results_df = pd.concat(results)
    

전체 소스 코드는 다음과 같습니다.

import pandas as pd
from geopy.distance import geodesic
from itertools import combinations
import multiprocessing as mp

df = pd.DataFrame(
    {
        "serial_number": [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
        "column_name": ["aa", "aa", "aa", "bb", "bb", "bb", "bb", "cc", "cc", "cc"],
        "lat": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        "lon": [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
    }
)

grp_lst_args = list(df.groupby("column_name").groups.items())
print(grp_lst_args)
# [('aa', [0, 1, 2]), ('cc', [7, 8, 9]), ('bb', [3, 4, 5, 6])]


def calc_dist(arg):
    grp, lst = arg
    return pd.DataFrame(
        [
            [
                grp,
                df.loc[c[0]].serial_number,
                df.loc[c[1]].serial_number,
                geodesic(df.loc[c[0], ["lat", "lon"]], df.loc[c[1], ["lat", "lon"]]),
            ]
            for c in combinations(lst, 2)
        ],
        columns=["column_name", "machine_A", "machine_B", "Distance"],
    )


pool = mp.Pool(processes=(mp.cpu_count() - 1))
results = pool.map(calc_dist, grp_lst_args)
pool.close()
pool.join()
results_df = pd.concat(results)

results_df

출력:

|      | column_name | machine_A | machine_B | Distance              |
| ---- | ----------- | --------- | --------- | --------------------- |
| 0    | aa          | 1         | 2         | 156.87614940188664 km |
| 1    | aa          | 1         | 3         | 313.70544546930296 km |
| 2    | aa          | 2         | 3         | 156.82932911607335 km |
| 0    | bb          | 4         | 5         | 156.66564184752647 km |
| 1    | bb          | 4         | 6         | 313.21433304645853 km |
| 2    | bb          | 4         | 7         | 469.6225353706956 km  |
| 3    | bb          | 5         | 6         | 156.54889742502786 km |
| 4    | bb          | 5         | 7         | 312.95759748703733 km |
| 5    | bb          | 6         | 7         | 156.40899678081678 km |
| 0    | cc          | 8         | 9         | 156.0601654009819 km  |
| 1    | cc          | 8         | 0         | 311.91099818906036 km |
| 2    | cc          | 9         | 0         | 155.85149814424545 km |
Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook

관련 문장 - Pandas DataFrame