我能够在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)
答案 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"))