2017-02-22 29 views
0

загружает данные из CSV в панда и сделать проверку на некоторых из таких областей, как это:Повышение эффективности проверки данных в пандах

(1.5s) loans['net_mortgage_margin'] = loans['net_mortgage_margin'].map(lambda x: convert_to_decimal(x)) 
(1.5s) loans['current_interest_rate'] = loans['current_interest_rate'].map(lambda x: convert_to_decimal(x)) 
(1.5s) loans['net_maximum_interest_rate'] = loans['net_maximum_interest_rate'].map(lambda x: convert_to_decimal(x)) 

(48s) loans['credit_score'] = loans.apply(lambda row: get_minimum_score(row), axis=1) 
(< 1s) loans['loan_age'] = ((loans['factor_date'] - loans['first_payment_date'])/np.timedelta64(+1, 'M')).round() + 1 
(< 1s) loans['months_to_roll'] = ((loans['next_rate_change_date'] - loans['factor_date'])/np.timedelta64(+1, 'M')).round() + 1 
(34s) loans['first_payment_change_date'] = loans.apply(lambda x: validate_date(x, 'first_payment_change_date', loans.columns), axis=1) 
(37s) loans['first_rate_change_date'] = loans.apply(lambda x: validate_date(x, 'first_rate_change_date', loans.columns), axis=1) 

(39s) loans['first_payment_date'] = loans.apply(lambda x: validate_date(x, 'first_payment_date', loans.columns), axis=1) 
(39s) loans['maturity_date'] = loans.apply(lambda x: validate_date(x, 'maturity_date', loans.columns), axis=1) 
(37s) loans['next_rate_change_date'] = loans.apply(lambda x: validate_date(x, 'next_rate_change_date', loans.columns), axis=1) 
(36s) loans['first_PI_date'] = loans.apply(lambda x: validate_date(x, 'first_PI_date', loans.columns), axis=1) 

(36s) loans['servicer_name'] = loans.apply(lambda row: row['servicer_name'][:40].upper().strip(), axis=1) 
(38s) loans['state_name'] = loans.apply(lambda row: str(us.states.lookup(row['state_code'])), axis=1) 
(33s) loans['occupancy_status'] = loans.apply(lambda row: get_occupancy_type(row), axis=1) 
(37s) loans['original_interest_rate_range'] = loans.apply(lambda row: get_interest_rate_range(row, 'original'), axis=1) 
(36s) loans['current_interest_rate_range'] = loans.apply(lambda row: get_interest_rate_range(row, 'current'), axis=1) 
(33s) loans['valid_credit_score'] = loans.apply(lambda row: validate_credit_score(row), axis=1) 
(60s) loans['origination_year'] = loans['first_payment_date'].map(lambda x: x.year if x.month > 2 else x.year - 1) 
(< 1s) loans['number_of_units'] = loans['unit_count'].map(lambda x: '1' if x == 1 else '2-4') 
(32s) loans['property_type'] = loans.apply(lambda row: validate_property_type(row), axis=1) 

Большинства из них являются функций, которые находят значение row, несколько прямого преобразование элемент для чего-то другого, но в целом, они выполняются для всей строки данных по строкам. Когда этот код был написан, кадры данных были достаточно малы, чтобы это не было проблемой. Однако код теперь адаптирован для принятия значительно больших таблиц, так что эта часть кода занимает слишком много времени.

Что такое лучший способ оптимизировать это? Моя первая мысль заключалась в том, чтобы идти по строкам, но применять все эти функции/преобразования в строке один раз (т. Е. Для строки в df, do func1, func2, ..., func21), но я не уверен, что это лучший способ справиться с этим. Есть ли способ избежать лямбда получить тот же результат, например, так как я предполагаю, что это лямбда, которая занимает много времени? Запуск Python 2.7 в случае, если это имеет значение.

Редактирование: большинство из этих вызовов работают примерно с той же скоростью за строку (несколько довольно быстрые). Это dataframe с 277 659 строк, который находится в 80-м процентиле по размеру.

Edit2: пример функции:

def validate_date(row, date_type, cols): 
    date_element = row[date_type] 
    if date_type not in cols: 
     return np.nan 
    if pd.isnull(date_element) or len(str(date_element).strip()) < 2: # can be blank, NaN, or "0" 
     return np.nan 
    if date_element.day == 1: 
     return date_element 
    else: 
     next_month = date_element + relativedelta(months=1) 
     return pd.to_datetime(dt.date(next_month.year, next_month.month, 1)) 

Это похоже на самый длинный вызов (origination_year), который извлекает значения из даты объекта (год, месяц и т.д.). Другие, например property_type, просто проверяют нерегулярные значения (например, «N/A», «NULL» и т. Д.), Но все-таки занимают немного времени, чтобы пройти через каждый.

+1

Если у вас уже есть функция 'convert_to_decimal', это просто беспорядок для записи' lambda x: convert_to_decimal (x) '. Это то же самое, что писать lambda y: (lambda x: convert_to_decimal (x)) (y) ' –

+0

Вопрос 1: ** что ** медленное? У вас есть 4-5 функций, которые мы не видим в коде, некоторые, например, 'convert_to_decimal'. Я надеюсь, что это быстро, другие, например' validate_date', где вы передаете ему столбец * может * быть проблемами, но мы может только предполагать. По крайней мере, было бы полезно посыпать некоторые вызовы 'print (time.time())'. –

+0

Отредактировано со временем за звонок. Дело не в том, что какой-то конкретный вызов медленный; это то, что в целом они добавляют больше времени, чем идеально, и по крайней мере кажется, что не называть каждую строку снова и снова, но я не знаю, заменит ли это 8,5 минут 7,6 минутами. – user2524282

ответ

0

td; lr: Рассмотрите возможность распределения обработки. Улучшение будет считывать данные в кусках и использовать несколько процессов. Источник http://gouthamanbalaraman.com/blog/distributed-processing-pandas.html

import multiprocessing as mp 

def process_frame(df): len(x) 

if __name__ == "__main__": 

    reader = read_csv(csv-file, chunk_size=CHUNKSIZE) 
    pool = mp.Pool(4) # use 4 processes 

    funclist = [] 
    for df in reader: 
      # process each data frame 
      f = pool.apply_async(process_frame,[df]) 
      funclist.append(f) 

    result = 0 
    for f in funclist: 
      result += f.get(timeout=10) # timeout in 10 seconds 

    print "There are %d rows of data"%(result) 

Другим вариантом может быть использование GNU parallel. вот еще один хороший пример использования GNU parallel