首页 > 解决方案 > 减去列之间的日期,条件是在 Python 中仅减去同一年内的日期

问题描述

所以我正在做一个需要识别多个设备启动指标的项目。为了识别启动指标,我需要这行代码来搜索一系列中唯一日期的列表并减去具有相同年份的日期。因此,如果 SV_DATE 是 2015/03/05,代码将查看“启动日期”系列以查找该年的匹配项(例如 2015/06/22),然后减去日期。between() 函数检查结果是否在 0 到 30 天的范围内,并返回一个布尔值。最后 astype(int) 如果 True 则返回 1

当我运行代码时,我遇到了两条错误消息。由于我比较了两列,第一个错误与真值不明确有关。

def day_diff(end,start):

    ed = pd.to_datetime(end)

    sd = pd.to_datetime(start)

    #if ed.dt.year == sd.year:

    return (ed-sd).dt.days

 data['AL030'] = day_diff(data['SV_DATE'],data_2.loc[(data_2['MFG'] == 'APPLE') & (pd.Series(pd.DatetimeIndex(data_2['Launch Date'])).dt.year == pd.Series(pd.DatetimeIndex(data['SV_DATE'])).dt.year), 'Launch Date']).between(0,30).astype(int)

为了让代码运行,我需要对年份进行硬编码,而不是通过日期列搜索代码。当我这样做时,代码就可以工作了。

data['AL030'] = day_diff(data['SV_DATE'],data_2.loc[(data_2['MFG'] == 'APPLE') & (pd.Series(pd.DatetimeIndex(data_2['Launch Date'])).dt.year == 2017), 'Launch Date'].apply(lambda x:x.date().strftime('%Y-%m-%d'))).between(0,30).astype(int)

我什至在添加 unique() 函数之前就收到了这个错误,这给了我一个新错误:'ValueError: cannot add indices of unequal length'

data['AL030'] = day_diff(data['SV_DATE'],data_2.loc[(data_2['MFG'] == 'APPLE') & (pd.Series(pd.DatetimeIndex(data_2['Launch Date'])).dt.year == 2017), 'Launch Date'].apply(lambda x:x.date().strftime('%Y-%m-%d')).unique()).between(0,30).astype(int)

如果我不想比较列之间的年份,那么这段代码就足够了:

data['AL030'] = day_diff(data['SV_DATE'],data_2.loc[(data_2['MFG'] == 'APPLE'), 'Launch Date']).between(0,60).astype(int)

归根结底,我正在尝试优化 R 中的这段代码以返回相同的值,而不使用像这样的 launch.ind 函数,同时双重添加年份条件以尝试减少运行时间:

day_diff = function(end,start){

  x = difftime(end,start,units=c("days"))

  return(x)

}

 

launch.ind = function(ship.date,launch.date,low,high){

  y = rep(0,length(data$SV_DATE))

  for (i in seq(length(data$SV_DATE))){

    y[i] = sum(ifelse((day_diff(ship.date[i],launch.date)>=low)&(day_diff(ship.date[i],launch.date)<=high),1,0))

    y[i] = ifelse(y[i] > 0, 1, 0)

  }

  return(y)

}
###############################

# Add launch indicators

data$AL030 = launch.ind(data$SV_DATE,unique(data_2$"Launch Date"[toupper(data_2$MFG)=="APPLE"]),0,30)

我感谢任何试图提供帮助的人,我愿意接受帮助澄清任何不清楚的建议

标签: pythonrpandasfunctiondatetime

解决方案


Python


假数据:

import pandas as pd

data_1 = pd.DataFrame({
    'SV_DATE': pd.to_datetime(['2015/03/05', '2015/03/10', '2016/01/01'])
})

data_2 = pd.DataFrame({
    'Launch Date': pd.to_datetime(['2015/03/05', '2015/12/01', '2016/01/01', '2017/01/01']),
    'MFG': ['APPLE', 'WINDOWS', 'APPLE', 'WINDOWS']
})

print(data_1)

     SV_DATE
0 2015-03-05
1 2015-03-10
2 2016-01-01

print(data_2)

  Launch Date      MFG
0  2015-03-05    APPLE
1  2015-12-01  WINDOWS
2  2016-01-01    APPLE
3  2017-01-01  WINDOWS

如果我做对了,您可以合并过滤器 data_2(仅带有 的行MFG==APPLE),按年份合并两个数据框,按年份计算日期之间的差异,然后验证它们是否在您想要的范围内(0,30)

data_1 = data_1.assign(Year = data_1.SV_DATE.dt.year, Index = data_1.index)
data_2 = data_2.assign(Year = data_2['Launch Date'].dt.year).query('MFG=="APPLE"')

data = data_1.merge(data_2, on='Year')
data['Diff'] = data.groupby('Year')[['Launch Date','SV_DATE']].transform('diff', axis=1)['SV_DATE'].dt.days
data['in_target_range'] = data.Diff.between(0,30)

输出:

     SV_DATE  Year  Index Launch Date    MFG  Diff  in_target_range
0 2015-03-05  2015      0  2015-03-05  APPLE     0             True
1 2015-03-10  2015      1  2015-03-05  APPLE     5             True
2 2016-01-01  2016      2  2016-01-01  APPLE     0             True

我想,有了这个输出,你可以做任何你想做的事情。data_1请注意,如果您愿意,我保留了索引列以便检索这些行。

R


使用 R 的类似方法:

library(dplyr)

# Fake data
data_1 <- data.frame(SV_DATE = as.Date(c('2015/03/05', '2015/03/10', '2016/01/01')))

data_2 <- data.frame (
  Launch_Date = as.Date(c('2015/03/05', '2015/12/01', '2016/01/01', '2017/01/01')),
  MFG = c('APPLE', 'WINDOWS', 'APPLE', 'WINDOWS')
)

# Merge and filters
data_2 <- data_2 %>%
  mutate(Year = format(Launch_Date, "%Y")) %>%
  filter(MFG=="APPLE")

data <- data_1 %>% 
  mutate(Year = format(SV_DATE, "%Y"), Index = 1:nrow(.)) %>%
  inner_join(., mutate(data_2, Year=format(Launch_Date, "%Y")), by = "Year") %>%
  group_by(Year) %>%
  mutate(Diff = as.integer(SV_DATE - Launch_Date)) %>%
  mutate(in_target_range = between(Diff, 0, 30))

输出是:

# A tibble: 3 x 7
# Groups:   Year [2]
  SV_DATE    Year  Index Launch_Date MFG    Diff in_target_range
  <date>     <chr> <int> <date>      <chr> <int> <lgl>          
1 2015-03-05 2015      1 2015-03-05  APPLE     0 TRUE           
2 2015-03-10 2015      2 2015-03-05  APPLE     5 TRUE           
3 2016-01-01 2016      3 2016-01-01  APPLE     0 TRUE           

我不知道你真正想要的launch.ind功能是什么,但它可能是这样的(?):

low = 0
high = 3

data$AL030 <- data %>% 
  group_by(SV_DATE) %>%
  summarise(launch.ind = sum(ifelse(between(Diff, low, high), 1, 0)), .groups='drop') %>%
  mutate(launch.ind = ifelse(launch.ind > 0, 1, 0)) %>%
  pull(launch.ind)

笔记


尽管此代码适用于我提供的虚假数据,但它可能不适用于您。无论如何,我相信它提供了一些通过修改它来实现你的目标的方法。

另外,请注意,我在两个代码块中都保留为布尔值,但您可以分别使用 Python 和 R轻松in_target_range地将其更改为整数。.astype(int)as.integer(...)


推荐阅读