DataFrame Loop 종류

Chalsu Chalsu·2021년 12월 27일
0

Python Issue

목록 보기
1/4

Pandas에서 Loop를 돌리는 방법

  • 단순 For Loop
    • Matrix에서 Elements를 하나씩 관찰하는 방식으로 행과 열이 늘어날수록 시간은 기하급수적으로 증가
    • 데이터가 많은 상태에서 실행하면 시간 소요가 엄청날 수 있다.
  • Iterrows
    • 행을 하나씩 관찰하기 위해 어쩔 수 없이 반복문을 돌려야 하는 경우 활용하는 방식
    • DataFrame의 행을 반복하며 행 자체를 포함하는 객체에 덧붙여 각 행의 색인을 반환하는 제너레이터
    • 단순 For Loop를 돌리는 것보다 속도가 빠르다.
    • 하지만 최선의 방법은 아니다.
  • apply
    • DataFrame의 특정 축(행 또는 열)을 따라 함수를 적용하는 방식
    • lambda를 활용해 특정 축에 따라 적용하거나 def를 활용해 함수를 만들어 적용 가능
    • 일반적으로 자주 활용
  • Vectorize
    • Pandas Series를 활용한 벡터화와 Numpy ndarrays를 활용한 벡터화가 존재
    • Pandas Vectorize
      • Pandas DataFrame과 Series는 모두 배열 기반의 방식
      • 개별 값마다 순차적으로 작동하는 대신 전체 배열 상에서 작동하도록 Pandas 내장 함수를 통해 변환된 후 실행
    • Numpy Vectorize
      • Pandas Vectorize와 마찬가지로 전체 배열 상에서 작동하도록 변환된 뒤 수행되며, C로 컴파일된 Numpy를 활용하기 때문에 빠른 연산 가능
      • Pandas Series의 색인 기능을 필요로 하지 않을 때 Pandas Vectorize보다 더 빠르게 연산 가능

Vectorize를 통해 문제를 해결한 케이스

  • 일반적인 상황에서는 apply()만 써도 해결되는 경우가 많다.
  • BOAZ 프로젝트로 카페 추천시스템을 진행할 당시, 공공데이터 포털에서 식당 데이터와 지하철 데이터를 다운 받은 후 식당과 가장 가까운 역을 매핑하는 작업 수행 지하철 역 데이터 정보
  • 135,810개의 식당과 550개의 지하철 역 간의 거리를 전부 구한 뒤 가장 가까운 역을 찾는다는 것은 135810 × 550135810 \ \times \ 550 번의 반복 연산을 수행해야 한다는 것을 뜻한다.
  • 같은 이름의 식당이라도 위치가 다르면 다른 식당이었기에 서로를 구분하기 위한 새로운 키 값으로 지하철 역을 매핑하는 작업이었기에 apply()문으로 일단 작업 수행
    from haversine import haversine
    
    def dist(x):
      res_point = x[["위도","경도"]]
      dist_list = [99999]
      for i in range(len(subway)):
        sub_point = subway.loc[i,["lat","lng"]]
        distance = haversine(res_point,sub_point)
        
        if distance < min(dist_list):
          min_dist_name = subway.loc[i,"name"]
          dist_list.append(distance)
          print("{} {} : {}".format(x["상호명"],min_dist_name,distance))
      return min_dist_name, min(dist_list)*1000
    
    data_nowon[["역 명","거리"]] = pd.DataFrame(list(data_nowon.apply(lambda x : dist(x),axis=1)))
  • 노원구 4251개 데이터에 대해서만 연산을 수행했을 때 40분에 가까운 시간이 소요 → 전체가 30배 정도되는 크기였기에 다른 방법이 필요했다.
    • 이때 찾은 방법이 Vectorize
  • 다음과 같이 Numpy Vectorize를 활용해 haversine 함수를 만든 뒤, 최솟값에 해당하는 역을 Return 시키도록 코드를 짰을 때, 13만개에 해당하던 식당에 대해서 1분도 체 걸리지 않았다.
    import numpy as np
    
    def haversine_np(lon1, lat1, lon2, lat2):
        lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2])
    
        dlon = lon2 - lon1
        dlat = lat2 - lat1
    
        a = np.sin(dlat/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2.0)**2
    
        c = 2 * np.arcsin(np.sqrt(a))
        km = 6367 * c
        return km * 1000
    
    def cal_dist(lat,lng):
        dist = haversine_np(lat,lng, subway['lat'].values, subway['lng'].values)
        station = subway.loc[np.argmin(dist),"name"]
        distance = dist[np.argmin(dist)]
        return pd.Series([station, distance])
    data[["역명","거리"]] = data.apply(lambda x : cal_dist(x["위도"], x["경도"]), axis=1)

→ 연산량이 많아질수록 Vectorize가 더 큰 힘을 발휘하는 것 같다.

profile
https://github.com/MrLee5693

0개의 댓글