R

时间:2016-12-29 20:16:56

标签: r grouping dummy-variable

我能够在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

这是我试图实现的解释。

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

现在在第2行,类型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')
  }
}

提前感谢您的帮助。为了表明我在堆栈流社区的精神中并不懒惰,这是我写的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)  

3 个答案:

答案 0 :(得分: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转换为宽格式

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 :(得分: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])
  }
}

答案 2 :(得分: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"))