Tăng tốc độ chuyển đổi datetime sang múi giờ hỗn hợp - Python pandas
Lưu ý: Đây là phần tiếp theo cho câu hỏi này .
Tóm tắt sự cố: Tôi có khung dữ liệu gấu trúc với dấu thời gian UNIX là số nguyên mà không có bất kỳ thông tin thời gian nào. Tôi cần chuyển đổi chúng thành các múi giờ cụ thể (và sau đó biến chúng thành các đối tượng ngây thơ của múi giờ). Vấn đề là việc thực hiện chuyển đổi này dưới dạng lặp lại trên mỗi dòng là khá chuyên sâu và hiện chiếm khoảng 60% thời gian xử lý của tôi (thậm chí nhiều hơn trong ví dụ đơn giản này). Tôi tin rằng điều này có thể được giảm bớt bằng cách sử dụng chức năng datetime bổ sung của gấu trúc, nhưng tôi đang gặp khó khăn trong việc tìm ra cách thực hiện điều này. Để đưa ra quy mô, tôi cần chạy mã trên hàng nghìn tệp có một vài / vài triệu quan sát mỗi tệp.
Thí dụ:
import pandas as pd
import time
#creating data:
n_obs=750000 # need to be a multiple of 15
l1=[1546555701, 1546378818, 1546574677, 1546399159, 1546572278]
l2=['America/Detroit','America/Chicago','America/Los_Angeles']
c1=l1*(int(n_obs/5))
c2=l2*(int(n_obs/3))
df=pd.DataFrame(list(zip(c1,c2)),columns=['timestamp','tz'])
print(df)
# operations:
sort_dict={}
tz_list=df['tz'].unique()
for x in tz_list:
df_temp=df[df['tz']==x]
sort_dict[x]=df_temp
def setTZ(row,x):
return row['date_time'].tz_convert(x).replace(tzinfo=None)
for x in [tz_list[0]]: # I just time the first iteration of the loop for simplicity
tic = time.perf_counter()
sort_dict[x]['date_time']=pd.to_datetime(df['timestamp'],unit='s',utc=True)
toc = time.perf_counter()
print(f'to_datetime() completed in {toc-tic:0.4f} seconds')
# the above works quite quickly, but the problem is in the following lines:
tic = time.perf_counter()
sort_dict[x]['date_time']=sort_dict[x].apply(lambda row: setTZ(row,x), axis=1)
toc = time.perf_counter()
print(f'setTZ() completed in {toc-tic:0.4f} seconds')
tic = time.perf_counter()
sort_dict[x]['date']=sort_dict[x].apply(lambda row: row['date_time'].date(),axis=1)
toc = time.perf_counter()
print(f'create date column with .date() completed in {toc-tic:0.4f} seconds')
tic = time.perf_counter()
sort_dict[x]['time']=sort_dict[x].apply(lambda row: row['date_time'].time(),axis=1)
toc = time.perf_counter()
print(f'create time column with .time() completed in {toc-tic:0.4f} seconds')
Đầu ra:
to_datetime() completed in 0.0311 seconds
setTZ() completed in 26.3287 seconds
create date column with .date() completed in 3.2471 seconds
create time column with .time() completed in 3.2625 seconds
# I also have a SettingWithCopyWarning error from my code, which I think comes from how I'm overwriting the dictionaries
Điểm rút ra : Hàm setTZ () cực kỳ chậm. Tôi nghĩ điều này là do tôi đang lặp lại từng dòng một trên mã để thực hiện chuyển đổi này. to_datetime () cực kỳ nhanh chóng. Nếu có một cách nào đó để kết hợp múi giờ và làm mất khả năng nhận biết thời gian (vì tôi sẽ so sánh các quan sát cùng lúc giữa các múi giờ) thì đó sẽ là lý tưởng. Tạo cột ngày và giờ chậm hơn so với hàm to_datetime (), nhưng nhanh so với hàm setTZ (). Tối ưu hóa chúng sẽ là tốt.
Giải pháp khả thi: Tôi đoán rằng tôi có thể khai thác một số hàm datetime của gấu trúc, chẳng hạn như tz_localize () và tz_convert (), nhưng tôi cần có thể chuyển đổi cột trong khung dữ liệu gấu trúc của mình thành Mảng datetime. Tôi không rõ mình có thể làm điều này như thế nào. Tôi chắc chắn rằng các giải pháp khác cũng tồn tại.
Trả lời
Đưa ra khung dữ liệu như được mô tả và được mở rộng đến 50k hàng vừa phải
from datetime import datetime
from backports.zoneinfo import ZoneInfo # backports not needed with Python 3.9
import pandas as pd
c1 = [1546555701, 1546378818, 1546574677, 1546399159, 1546572278]*10000
c2 = ['America/Detroit','America/Chicago','America/Los_Angeles','America/Los_Angeles','America/Detroit']*10000
df3 = pd.DataFrame({'utc': c1, 'tz': c2})
df3['datetime'] = pd.to_datetime(df3['utc'], unit='s', utc=True)
ngoài việc sử dụng tz_convertlặp đi lặp lại các pandas được tích hợp sẵn , bạn cũng có thể sử dụng khả năng hiểu danh sách với itertuples của gấu trúc + datetime & zoneinfo của Python :
def toLocalTime_pd(row): # as given
return row['datetime'].tz_convert(row['tz']).replace(tzinfo=None)
def localTime_dt(df):
return [datetime.fromtimestamp(row.utc, tz=ZoneInfo(row.tz)).replace(tzinfo=None) for row in df.itertuples()]
Khi so sánh trực tiếp, danh sách comp hoạt động tốt hơn ~ x8 cho ví dụ tổng hợp df:
%timeit df3.apply(lambda r: toLocalTime_pd(r), axis=1)
1.85 s ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit localTime_dt(df3)
217 ms ± 7.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)