Оптимизация с использованием Pandas Data Frame - PullRequest
0 голосов
/ 13 сентября 2018

У меня есть следующая функция, которая загружает CSV в кадр данных, а затем выполняет некоторые вычисления.Выполнение расчетов в CSV занимает около 4-5 минут с чуть более 100 000 строк.Я надеялся, что есть более быстрый способ.

def calculate_adeck_errors(in_file):
    print(f'Starting Data Calculations: {datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y")}')
    pd.set_option('display.max_columns', 12)

    # read in the raw csv
    adeck_df = pd.read_csv(in_file)
    #print(adeck_df)

    #extract only the carq items and remove duplicates
    carq_data = adeck_df[(adeck_df.MODEL == 'CARQ') & (adeck_df.TAU == 0)].drop_duplicates(keep='last')
    #print(carq_data)

    #remove carq items from original
    final_df = adeck_df[adeck_df.MODEL != 'CARQ']
    #print(final_df)

    row_list = []
    for index, row in carq_data.iterrows():
        position_time = row['POSDATETIME']
        for index, arow in final_df.iterrows():
            if arow['POSDATETIME'] == position_time:
                # match, so do calculations
                storm_id = arow['STORMID']
                model_base_time = arow['MODELDATETIME']
                the_hour = arow['TAU']
                the_model = arow['MODEL']
                point1 = float(row['LAT']), float(row['LON'])
                point2 = float(arow['LAT']), float(arow['LON'])
                if arow['LAT'] == 0.0:
                    dist_error = None
                else:
                    dist_error = int(round(haversine(point1, point2, miles=True)))

                if arow['WIND'] != 0:
                    wind_error = int(abs(int(row['WIND']) - int(arow['WIND'])))
                else: wind_error = None

                if arow['PRES'] != 0:
                    pressure_error = int(abs(int(row['PRES']) - int(arow['PRES'])))
                else:
                    pressure_error = None

                lat_carq = row['LAT']
                lon_carq = row['LON']
                lat_model = arow['LAT']
                lon_model = arow['LON']
                wind_carq = row['WIND']
                wind_model = arow['WIND']
                pres_carq = row['PRES']
                pres_model = arow['PRES']

                row_list.append([storm_id, model_base_time, the_model, the_hour, lat_carq, lon_carq, lat_model, lon_model, dist_error,
                             wind_carq, wind_model, wind_error, pres_carq, pres_model, pressure_error])

    result_df = pd.DataFrame(row_list)
    result_df = result_df.where((pd.notnull(result_df)), None)
    result_cols = ['StormID', 'ModelBasetime', 'Model' , 'Tau',
               'LatCARQ', 'LonCARQ', 'LatModel', 'LonModel', 'DistError',
               'WindCARQ', 'WindModel','WindError',
               'PresCARQ', 'PresModel','PresError']

    result_df.columns = result_cols

calculate_adeck_errors(infile)

Чтобы уточнить, что я делаю: 1. Записи CARQ являются контрольными (фактическими).2. Остальные модели являются догадками.3. Я сравниваю контроль (CARQ) с догадками, чтобы увидеть их ошибки.4. Основой сравнения является MODELBASETIME = POSBASETIME 4. Образец файла, который я обрабатываю, находится здесь: http://vortexweather.com/downloads/adeck/aal062018.csv

Я надеялся, что есть более быстрый способ, чем я делаю, или другойМетод панд помимо iterrows

Большое спасибо за предложение.Bryan

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

Этот код занимает около 10 секунд для запуска всего набора данных!

Код выглядит очень похоже на то, что вы написали, за исключением того, что все операции в main_function имеютбыл векторизован.См. Быстрый, гибкий, простой и интуитивно понятный: как ускорить ваши проекты Pandas

2018-09-13_adeck_error_calculations.ipynb

import pandas as pd
import numpy as np
import datetime
from haversine import haversine


def main_function(df, row):
    """
    The main difference here is that everything is vectorized
    Returns: DataFrame
    """

    df_new = pd.DataFrame()
    df_storage = pd.DataFrame()

    pos_datetime = df.POSDATETIME.isin([row['POSDATETIME']])  # creates a Boolean map
    array_len = len(pos_datetime)
    new_index = pos_datetime.index

    df_new['StormID'] = df.loc[pos_datetime, 'STORMID']
    df_new['ModelBaseTime'] = df.loc[pos_datetime, 'MODELDATETIME']
    df_new['Model'] = df.loc[pos_datetime, 'MODEL']
    df_new['Tau'] = df.loc[pos_datetime, 'TAU']

    # Distance
    df_new['LatCARQ'] = pd.DataFrame(np.full((array_len, 1), row['LAT']), index=new_index).loc[pos_datetime, 0]
    df_new['LonCARQ'] = pd.DataFrame(np.full((array_len, 1), row['LON']), index=new_index).loc[pos_datetime, 0]
    df_new['LatModel'] = df.loc[pos_datetime, 'LAT']
    df_new['LonModel'] = df.loc[pos_datetime, 'LON']


    def calc_dist_error(row):
        return round(haversine((row['LatCARQ'], row['LonCARQ']), (row['LatModel'], row['LonModel']), miles=True)) if row['LatModel'] != 0.0 else None

    df_new['DistError'] = df_new.apply(calc_dist_error, axis=1)

    # Wind
    df_new['WindCARQ'] = pd.DataFrame(np.full((array_len, 1), row['WIND']), index=new_index).loc[pos_datetime, 0]
    df_new['WindModel'] = df.loc[pos_datetime, 'WIND']
    df_storage['row_WIND'] = pd.DataFrame(np.full((array_len, 1), row['WIND']), index=new_index).loc[pos_datetime, 0]
    df_storage['df_WIND'] = df.loc[pos_datetime, 'WIND']


    def wind_error_calc(row):
        return (row['row_WIND'] - row['df_WIND']) if row['df_WIND'] != 0 else None

    df_new['WindError'] = df_storage.apply(wind_error_calc, axis=1)

    # Air Pressure
    df_new['PresCARQ'] = pd.DataFrame(np.full((array_len, 1), row['PRES']), index=new_index).loc[pos_datetime, 0]
    df_new['PresModel'] = df.loc[pos_datetime, 'PRES']
    df_storage['row_PRES'] = pd.DataFrame(np.full((array_len, 1), row['PRES']), index=new_index).loc[pos_datetime, 0]
    df_storage['df_PRES'] = df.loc[pos_datetime, 'PRES']


    def pres_error_calc(row):
        return abs(row['row_PRES'] - row['df_PRES']) if row['df_PRES'] != 0 else None

    df_new['PresError'] = df_storage.apply(pres_error_calc, axis=1)

    del(df_storage)

    return df_new


def calculate_adeck_errors(in_file):
    """
    Retruns: DataFrame
    """
    print(f'Starting Data Calculations: {datetime.datetime.now().strftime("%I:%M:%S%p on %B %d, %Y")}')
    pd.set_option('max_columns', 20)
    pd.set_option('max_rows', 300)

    # read in the raw csv
    adeck_df = pd.read_csv(in_file)
    adeck_df['MODELDATETIME'] = pd.to_datetime(adeck_df['MODELDATETIME'], format='%Y-%m-%d %H:%M')
    adeck_df['POSDATETIME'] = pd.to_datetime(adeck_df['POSDATETIME'], format='%Y-%m-%d %H:%M')

    #extract only the carq items and remove duplicates
    carq_data = adeck_df[(adeck_df.MODEL == 'CARQ') & (adeck_df.TAU == 0)].drop_duplicates(keep='last')    
    print('Len carq_data: ', len(carq_data))

    #remove carq items from original
    final_df = adeck_df[adeck_df.MODEL != 'CARQ']
    print('Len final_df: ', len(final_df))

    df_out_new = pd.DataFrame()

    for index, row in carq_data.iterrows():

        test_df = main_function(final_df, row)  # function call

        df_out_new = df_out_new.append(test_df, sort=False)


    df_out_new = df_out_new.reset_index(drop=True)
    df_out_new = df_out_new.where((pd.notnull(df_out_new)), None)

    print(f'Finishing Data Calculations: {datetime.datetime.now().strftime("%I:%M:%S%p on %B %d, %Y")}')
    return df_out_new

in_file = 'aal062018.csv'

df = calculate_adeck_errors(in_file)
>>>Starting Data Calculations: 02:18:30AM on September 13, 2018
>>>Len carq_data:  56
>>>Len final_df:  137999
>>>Finishing Data Calculations: 02:18:39AM on September 13, 2018

print(len(df))
>>>95630

print(df.head(20))

Пожалуйста, не забудьте проверить принятое решение.Наслаждайтесь!

0 голосов
/ 13 сентября 2018

Похоже, вы создаете два фрейма данных из одного и того же фрейма, а затем обрабатываете их.Две вещи, которые могут сократить ваше время.

Во-первых, вы перебираете оба фрейма данных и проверяете условие:

for _, row in carq_data.iterrows():
    for _, arow in final_df.iterrows():
        if arow['POSDATETIME'] == row['POSDATETIME']:
            # do something by using both tables

По сути, это реализация объединения.Вы присоединяетесь carq_data с final_df к 'POSDATETIME'.

. В качестве первого шага вы должны объединить таблицы:

merged = carq_data.merge(final_df, on=['POSDATETIME'])

На этом этапе вы получите несколько строк длякаждый похож 'POSDATETIME'.Ниже, давайте предположим, что столбец b равен POSDATETIME:

>>> a
   a   b
0  1  11
1  1  33
>>> b
   a  b
0  1  2
1  1  3
2  1  4
>>> merged = a.merge(b, on=['a'])
>>> merged
   a  b_x  b_y
0  1   11    2
1  1   11    3
2  1   11    4
3  1   33    2
4  1   33    3
5  1   33    4

Теперь, чтобы выполнить ваши условные вычисления, вы можете использовать функцию apply().

Сначала определитефункция:

def calc_dist_error(row):
    return int(round(haversine(row['b_x'], row['b_y'], miles=True))) if row['a'] != 0.0 else None

Затем примените ее к каждой строке:

merged['dist_error'] = merged.apply(calc_dist_error, axis=1)

Продолжая мой небольшой пример:

>>> merged['c'] = [1, 0, 0, 0, 2, 3]
>>> merged
   a  b_x  b_y  c
0  1   11    2  1
1  1   11    3  0
2  1   11    4  0
3  1   33    2  0
4  1   33    3  2
5  1   33    4  3
>>> def foo(row):
...     return row['b_x'] - row['b_y'] if row['c'] != 0 else None
...
>>> merged['dist_error'] = merged.apply(foo, axis=1)
>>> merged
   a  b_x  b_y  c  dist_error
0  1   11    2  1         9.0
1  1   11    3  0         NaN
2  1   11    4  0         NaN
3  1   33    2  0         NaN
4  1   33    3  2        30.0
5  1   33    4  3        29.0

Это должно помочь вам сократить время выполнения (см. также this для проверки использования %timeit).Надеюсь это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...