2016-12-29 111 views
1

我能够在Python中执行此数据ETL工作。但是,因为我需要与R集成,而我是R的新手,所以我在这里发布问题。我想爆炸基于START_DATE日期和结束日期的日期,并做累积频率求和自变量“类型”虚拟变量在R中的日期累计总和

原始数据有3列与变量名日期,结束日期和导出虚拟变量类型

start_date, end_date, type 
1/1/2016, 1/3/2016, A 
1/2/2016, 1/2/2016, B 
1/2/2016, 1/3/2016, A 

这是我试图实现的解释。

对于记录的第一行,类型A每天从1/1到1/3出现(包括开始日期和结束日期)。

现在在第二行上,类型B只出现在1/2上。

到目前为止,1/1有一个'A',1/2有一个'A'和一个'B',1/3有一个'A'。

这样的过程重复其余的记录。实际上,我在变量“type”中有很多这样的行和很多不同的值

基本上,我需要一个有效的算法来为每天的变量“type”中的所有变量进行频率计数,以日期作为索引列的数据帧以及变量“type”中所有唯一变量的相应频率计数。希望澄清。

我需要与第一行中输入以下格式的数据帧作为新的头

date,  A,  B 
1/1/2016, 1,  0 
1/2/2016, 2,  1 
1/3/2016, 2,  0 

似乎如预期@ Tiffany的解决方案没有奏效。他/她的嵌套循环代码部分分解为我的下面的示例代码。

start_date end_date type 
1/1/16 1/3/16 A 
1/1/16 1/3/16 A 
1/1/16 1/8/16 B 
1/1/16 1/14/16 B 
1/5/16 1/19/16 B 
1/7/16 1/13/16 C 
1/9/16 1/18/16 A 
1/13/16 1/19/16 D 
1/13/16 1/19/16 A 
1/14/16 1/22/16 B 
1/15/16 1/29/16 B 
1/16/16 1/22/16 D 

正确的部分是:

results <- data.frame(date = dates) 

for(t in unique(df$type)) { 
    for(d in dates) { 
    results[results$date == d, t] <- 
     length(df[df$start_date <= d & df$end_date >= d & df$type == t],'type') 
    } 
} 

感谢提前的帮助。为了证明我不是在stackover流社会的精神懒散,这是Python版本,我写道:

import pandas as pd 

df = pd.read_csv("dates.csv") 

factor_type = list(df['type'].unique()) 

columns = ['date'] 
columns.extend(factor_type) 


result = [] 

dates_dict = {} 
i = 0 


for index,row in df.iterrows(): 
    start_end = pd.date_range(row['start_date'], row['end_date']) 
    factor = row['variable_type'] 
    factor_index = factor_type.index(factor) 
    for x in start_end: 
     date_obj = x.date() 
     date_str = '%s/%s/%s' % (date_obj.month, date_obj.day,date_obj.year) 
     if date_str in dates_dict: 
      row_index = dates_dict[date_str] 
      result[row_index+1][factor_index+1]+=1 
     else: 
      dummy_row = [0]*len(factor_type) 
      dummy_row[factor_index]=1 
      result.append([date_str]+dummy_row) 
      dates_dict[date_str]=i+1 


result_df = pd.DataFrame(result,columns=columns) 
+0

使用dcast转换到宽幅没有在你的Python代码仔细研读,你的输入和期望输出之间的映射关系不明确。请说明。 – MichaelChirico

+0

不应该'A'列在第二行有1?如果是这样,我认为以下是一个选项'library(data.table); dcast(唯一(熔化(setDT(df)[,indx:= .I],c(“type”,“indx”)),by = c(“indx”,“value”)),value_type) –

+0

这里start_date和end_date都包含在计数中,换句话说,两端都是关闭间隔。 – Jin

回答

4

这里有两种使用数据表的方式 - 一种是有效的但很难读取,另一种效率较低但易于阅读。

首先,两列转换为正确的日期类(我使用data.tables as.IDate函数内的整数表示,而数字之一)

library(data.table) 
cols <- c("start_date", "end_date") 
setDT(df)[, (cols) := lapply(.SD, as.IDate, format = "%m/%d/%Y"), .SDcols = cols] 

效率较低的溶液

一个简单的(但不是很有效的)方法是逐行扩展日期(已经提供),然后做一个简单的dcast,它既非常有效,也不关心有多少级别你有type

res <- df[, .(Date = seq.int(start_date, end_date, 1L), type), by = 1:nrow(df)] 
dcast(res, Date ~ type, length) 
# Using 'type' as value column. Use 'value.var' to override 
#   Date A B 
# 1: 2016-01-01 1 0 
# 2: 2016-01-02 2 1 
# 3: 2016-01-03 2 0 

更有效的解决方案

此解决方案不按行操作涉及,而工作在使用foverlaps功能日期的整个范围。第一步(也一样,已经提供了)是创建一个全面的范围,设置开始和结束范围,并设置一个键(用于进一步的操作)

Intervals <- data.table(start_date = df[, seq.int(min(start_date), max(end_date), 1L)]) # overall range 
Intervals[, end_date := start_date] # set start/end ranges as same values 
setkey(Intervals, start_date, end_date) # key 

现在,所有剩下的工作就是运行foverlaps并再次

dcast(foverlaps(df, Intervals), start_date ~ type, length) 
# Using 'type' as value column. Use 'value.var' to override 
# start_date A B 
# 1: 2016-01-01 1 0 
# 2: 2016-01-02 2 1 
# 3: 2016-01-03 2 0 
1

我不知道我完全理解你正在寻找的东西(你说“累计总和”但我认为你真的希望计算未清项目的数量。)

如果是这样的话,这里有一些(相对较脏的)代码会给你你想要的,对于你最早的start_date和你的最新的end_date。

library(lubridate) 
start_date <- c("1/1/2016", "1/2/2016", "1/2/2016") 
end_date <- c("1/3/2016", "1/2/2016", "1/3/2016") 
type <- c("A", "B", "A") 

将字符串转换为日期以使下一步更容易。

df <- data.frame(start_date, end_date, type) 
df$start_date <- as.Date(mdy(df$start_date)) 
df$end_date <- as.Date(mdy(df$end_date)) 

在最早的start_date和最近的end_date之间创建一个日期向量。

dates <- seq(from = min(c(df$start_date, df$end_date)), 
      to = max(c(df$start_date, df$end_date)), 
      by = 1) 

获取你想要的格式的数据:

results <- data.frame(date = dates, openA = NA, openB = NA) 
for(d in dates) { 
    results$openA[results$date == d] <- 
    length(df[df$start_date <= d & df$end_date >= d & df$type == "A"]) 

    results$openB[results$date == d] <- 
    length(df[df$start_date <= d & df$end_date >= d & df$type == "B"]) 
} 

对于类型的任意号码,你可以这样做:

results <- data.frame(date = dates) 

for(t in unique(df$type)) { 
    for(d in dates) { 
    results[results$date == d, t] <- 
     length(df[df$start_date <= d & df$end_date >= d & df$type == t]) 
    } 
} 
+0

感谢您的评论。在这里,我只是简单介绍一下这个示例。实际上,变量“type”中有很多独特的值,那么使用循环遍历所有这些值会更好吗? – Jin

+0

在类型的外部循环中添加了以上替代方法。 – tiffany

+0

它看起来在你的代码中有一些错误。请参阅我的修改后的帖子,其中包含更多示例数据以及问题出在哪里。 – Jin

0

我想提供一个dplyr的解决方案。

首先,我很高兴借用蒂芙尼的工作来构造数据框df。然后

  • 做一个清单,从开始日期,结束

    df2<-df%>% 
         rowwise()%>% 
         mutate(dates = list(as_date(start_date:end_date))) 
    
  • 列出所有这些日期,日期附加正确的类型,然后组和总结

    df3<-bind_rows(apply(df2,1,function(x){ 
            data.frame(Date = unlist(x$dates))%>%mutate(type=x$type[1]) 
        }))%>% 
         group_by(Date)%>% 
         summarise(A = sum(type=="A"), 
           B = sum(type=="B")) 
    
+0

我相信这会给你不同的结果,因为它按日期计算每种类型的开始或结束。即将重复计算开始日期和结束日期相同的情况,并忽略开始日期和结束日期之间的日期。 但是,如果您正在处理大量日期或类型,我会强烈推荐某种向量化(即应用)。 – tiffany

+0

@tiffany你是对的。我重新回答了我的答案 – Dries