【问题标题】:cumulative sum over dummy variable by date in RR中按日期对虚拟变量的累积和
【发布时间】:2016-12-29 20:16:55
【问题描述】:

我能够在 Python 中完成这项数据 ETL 工作。但是,由于我需要与 R 集成并且我是 R 的新手,所以我在这里发布了这个问题。我想根据 start_date 和 end_date 分解日期,并对从变量“type”派生的虚拟变量进行累积频率求和

原始数据有 3 列,变量名为 start_date、end_date 和 type

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(包括开始日期和结束日期)。

现在在第 2 行,类型 B 仅出现在 1/2。

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

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

基本上,我需要一个有效的算法来对变量“类型”中的所有变量进行频率计数,生成一个以日期为索引列的数据框,以及变量“类型”中所有唯一变量中的相应频率计数”。希望它澄清。

我需要以下格式的数据框,第一行作为新标题

 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 flow 社区的精神上并不懒惰,这是我写的 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)  

【问题讨论】:

  • 如果不仔细研究您的 python 代码,您的输入和所需输出之间的映射就不​​清楚了。请详细说明。
  • 不应该A列在第二行有1吗?如果是这样,我认为下面是一个选项library(data.table) ; dcast(unique(melt(setDT(df)[, indx := .I], c("type", "indx")), by = c("indx", "value")), value ~ type)
  • 这里 start_date 和 end_date 都包含在计数中,也就是说,两端都是闭区间。

标签: r grouping dummy-variable


【解决方案1】:

这里有两种使用数据表的方法——一种效率高但更难阅读,第二种效率低但更容易阅读。

首先,将两列都转换为正确的日期类(我使用 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,这既非常有效,也不关心你在@中有多少级别987654324@

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 转换为宽格式

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

【讨论】:

    【解决方案2】:

    我不确定我是否完全理解您要查找的内容(您说的是“累计金额”,但我认为您确实希望计算未清项目的数量。)

    如果是这种情况,这里有一些(相对肮脏的)代码可以为您提供您想要的,对于您最早的 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])
      }
    }
    

    【讨论】:

    • 感谢您的评论。在这里,我只是给出样本的一个小快照。实际上,变量“type”中有很多唯一值,那么使用循环遍历所有这些值会更好吗?
    • 在上面添加了一个带有外循环的替代方法。
    • 您的代码中似乎存在一些错误。请查看我修改后的帖子,其中包含更多示例数据以及问题所在。
    【解决方案3】:

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

    首先,我很高兴地借用了 tiffany 的工作来构建数据框 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"))
      

    【讨论】:

    • 我相信这会给你不同的结果,因为它按日期计算每种类型的开始或结束。 IE。将重复计算开始日期和结束日期相同的情况,并忽略开始日期和结束日期之间的日期。但是,如果您正在处理大量日期或类型,我强烈建议您进行某种矢量化(即应用)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    相关资源
    最近更新 更多