2016-06-01 1 views
2

У меня есть Dataframe в Pandas с буквой и двумя датами в виде столбцов. Я хотел бы рассчитать рабочие дни между двумя столбцами даты для предыдущей строки, используя shift(), при условии, что значение Letter будет одинаковым (с использованием .groupby()). Я делал это с .apply(). Это сработало, пока я не передал некоторые данные, в которых отсутствовала одна из дат. Я переместил все в функцию, чтобы обрабатывать недостающее значение с помощью предложения try/except, но теперь моя функция возвращает NaN для всего. Похоже, что значение None для даты влияет на каждый вызов функции, тогда как я думаю, что это будет делать только тогда, когда Letter от .groupby() - A.Работа с значениями None при использовании Pandas Groupby и приложением с функцией

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

def business_days(x): 
    try: 
     return pd.DataFrame(np.busday_count(x['First Date'].tolist(), x['Last Date'].tolist())).shift().reset_index(drop=True) 
    except ValueError: 
     return None 

df = pd.DataFrame(data=[['A', datetime(2016, 1, 7), None], 
         ['A', datetime(2016, 3, 1), datetime(2016, 3, 8)], 
         ['B', datetime(2016, 5, 1), datetime(2016, 5, 10)], 
         ['B', datetime(2016, 6, 5), datetime(2016, 6, 7)]], 
        columns=['Letter', 'First Date', 'Last Date']) 

df['First Date'] = df['First Date'].apply(lambda x: x.to_datetime().date()) 
df['Last Date'] = df['Last Date'].apply(lambda x: x.to_datetime().date()) 

df['Gap'] = df.groupby('Letter').apply(business_days) 

print df 

Фактический выход:

Letter First Date Last Date Gap 
0  A 2016-01-07   NaT NaN 
1  A 2016-03-01 2016-03-08 NaN 
2  B 2016-05-01 2016-05-10 NaN 
3  B 2016-06-05 2016-06-07 NaN 

Желаемая Выход:

Letter First Day Last Day Gap 
0  A 2016-01-07   NAT NAN 
1  A 2016-03-01 2016-03-08 NAN 
2  B 2016-05-01 2016-05-10 NAN 
3  B 2016-06-05 2016-06-07 7 
+0

В текущем коде, если письмо группа имеет единственный 'NaT' в одной строке, то функция busday_count поднимает ValueError и возвращается None , Вы хотите, чтобы вся группа Letter имела значения Gap NaN или вы хотите вычислить рабочие дни для строк, отличных от NaT? – unutbu

+0

@unutbu Только для строк, отличных от NaT – user2242044

ответ

3
  • Игнорирование NaT сек На данный момент, обратите внимание, что расчет np.busday_count может быть сделано на целых столбцов от dfдо с применением groupby. Это будет сэкономить время, так как это заменяет многие звонки на np.busday_count (по одному для каждой группы ) одним звонком до np.busday_count. Один вызов функции, примененный к массиву , обычно быстрее, чем многие вызовы функций на небольших массивах.

  • Чтобы справиться с NaT с, вы могли бы использовать pd.notnull для идентификации строк, которые имеют NaT с и маскировать First Date с и Last Date сек, так что только действительные даты отправляются np.busday_count. Затем вы можете заполнить NaN s для тех строк, где даты были NaT s.

  • После вычислим все день отсчеты бизнес, все, что нам нужно сделать, это группа по Letter и сдвига приведены значения вниз по одному. Это можно сделать с помощью groupby/transform('shift').


import datetime as DT 
import numpy as np 
import pandas as pd 

def business_days(start, end): 
    mask = pd.notnull(start) & pd.notnull(end) 
    start = start.values.astype('datetime64[D]')[mask] 
    end = end.values.astype('datetime64[D]')[mask] 
    result = np.empty(len(mask), dtype=float) 
    result[mask] = np.busday_count(start, end) 
    result[~mask] = np.nan 
    return result 

df = pd.DataFrame(data=[['A', DT.datetime(2016, 1, 7), None], 
         ['A', DT.datetime(2016, 3, 1), DT.datetime(2016, 3, 8)], 
         ['B', DT.datetime(2016, 5, 1), DT.datetime(2016, 5, 10)], 
         ['B', DT.datetime(2016, 6, 5), DT.datetime(2016, 6, 7)]], 
        columns=['Letter', 'First Date', 'Last Date']) 

df['Gap'] = business_days(df['First Date'], df['Last Date']) 
print(df) 
# Letter First Date Last Date Gap 
# 0  A 2016-01-07  NaT NaN 
# 1  A 2016-03-01 2016-03-08 5.0 
# 2  B 2016-05-01 2016-05-10 6.0 
# 3  B 2016-06-05 2016-06-07 1.0 

df['Gap'] = df.groupby('Letter')['Gap'].transform('shift') 
print(df) 

печатает

Letter First Date Last Date Gap 
0  A 2016-01-07  NaT NaN 
1  A 2016-03-01 2016-03-08 NaN 
2  B 2016-05-01 2016-05-10 NaN 
3  B 2016-06-05 2016-06-07 6.0 
+1

Мне нравится использовать вычисления для всего столбца за один шаг. Однако, я получаю ошибку значения, 'ValueError: не могу вычислить счет рабочего дня с датой NaT (не время). – user2242044

+0

Хм, я не могу воспроизвести эту ошибку. Получаете ли вы это при запуске кода, который я разместил выше? – unutbu

+0

Если вы получаете этот ValueError на вышеописанный код, какая версия Pandas и NumPy вы используете? Если вы получаете этот ValueError только при применении 'business_days' к вашим собственным данным (но не по коду выше), что такое dtype аргументов, переданных' business_days'? – unutbu