Работа с значениями None при использовании Pandas Groupby и применение с функцией

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

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
 user224204401 июн. 2016 г., 21:50
@unutbu Только для не-NaT строк
 unutbu01 июн. 2016 г., 21:44
В вашем текущем коде, если группа букв имеет одинNaT в единственной строке функция busday_count вызывает ValueError и None возвращается. Вы хотите, чтобы вся группа Letter имела значения Gap, равные NaN, или вы хотите рассчитать рабочие дни для не-NaT строк?

Ответы на вопрос(1)

Решение Вопроса

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

Чтобы справиться сNaTс, вы могли бы использоватьpd.notnull определить строки, которые имеютNaTи замаскироватьFirst Dateс иLast Dates, так что только действительные даты отправляютсяnp.busday_count, Вы можете заполнитьNaNs для тех строк, где были датыNaTs.

После того, как мы вычислим все количество рабочих дней, все, что нам нужно сделать, это сгруппировать по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
 user224204401 июн. 2016 г., 22:31
Мне нравится применять расчет ко всему столбцу за один шаг. Тем не менее, я получаю значение ошибки,ValueError: Cannot compute a business day count with a NaT (not-a-time) date
 cyril17 апр. 2017 г., 22:30
Хорошо, обновление до Numpy 1.12.1 решило эту проблему.
 cyril17 апр. 2017 г., 21:45
Я также получаю ту же ошибку, что и пользователь выше. Я использую Панды 0.19.2 и Numpy 1.11.3.<ipython-input-118-70ecbc042cd4> in business_days(start, end) 4 end = end.values.astype('datetime64[D]')[mask] 5 result = np.empty(len(mask), dtype=float) ----> 6 result[mask] = np.busday_count(start, end) 7 result[~mask] = np.nan 8 return result ValueError: Cannot compute a business day count with a NaT (not-a-time) date
 unutbu02 июн. 2016 г., 00:01
Хм, я не могу воспроизвести эту ошибку. Вы получаете это при запуске кода, который я разместил выше?
 unutbu02 июн. 2016 г., 00:08
Если вы получаете это ValueError в приведенном выше коде, то какую версию Pandas и NumPy вы используете? Если вы получаете только этот ValueError при примененииbusiness_days к вашим собственным данным (но не к приведенному выше коду), каков dtype аргументов, передаваемыхbusiness_days?

Ваш ответ на вопрос