使用Pandas Groupby和Apply with a Function时处理None值

时间:2016-06-01 18:48:13

标签: python python-2.7 pandas

我在Dataframe中有一个Pandas,其中包含一个字母和两个日期作为列。我想使用shift()计算上一行的两个日期列之间的营业日,前提是Letter值相同(使用.groupby())。我是用.apply()做的。这一直有效,直到我传递了一些缺少其中一个日期的数据。我将所有内容移动到一个函数来使用try/except子句处理缺失的值,但现在我的函数返回NaN的所有内容。看来日期的None值会影响函数的每次调用,而我认为只有当Letter的{​​{1}}为.groupby()时才会这样做。< / p>

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

1 个答案:

答案 0 :(得分:4)

  • 暂时忽略NaT,请注意np.busday_count计算 在应用df之前,可以在groupby 的整列上完成。这将 节省时间,因为这取代了许多对np.busday_count的呼叫(每个呼叫一个) 只需拨打一次np.busday_count即可。一个函数调用应用于a 大数组通常比小数组上的许多函数调用快。

  • 要处理NaT,您可以使用pd.notnull来识别哪些行 拥有NaT并屏蔽First DateLast Date s,以便只有效 日期将发送至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