in R将另一个因子中每个因子的行数相加,并在缺少“特定”数据时返回0

时间:2013-01-26 00:39:39

标签: r zoo

我遇到了一个潜在的问题,我希望你能帮助我:)

例如,我有以下数据表显示多个商店,每次访问者进入商店时,都会记录时间和日期。这意味着每行/每行都是1个访问其中一个商店的访客。

data <- structure(list(store.ID = c("1", "1", "1", "1", "1", 
"2", "2", "2", "2", "2", "3", "3", "3", 
"3", "3", "4", "4", "4", "4", "4"), Time = structure(c(6L, 
7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 1L, 2L, 3L, 4L, 5L, 
16L, 17L, 18L, 19L, 20L), .Label = c("  12:09:19", "  12:09:25", 
"  13:09:30", "  13:09:35", "  14:09:40", " 12:00:03", " 12:00:09", 
" 12:00:14", " 14:00:25", " 16:00:32", " 12:27:19", " 13:27:25", 
" 14:27:41", " 14:27:46", " 17:27:59", " 12:46:10", " 12:46:19", " 13:46:29", 
" 14:46:39", " 15:46:50"), class = "factor"), Date = structure(c(1351728000, 
1351728000, 1351728000, 1351728000, 1351728000, 1351814400, 1351814400, 
1351814400, 1351814400, 1351814400, 1351814400, 1351814400, 1351814400, 
1351814400, 1351814400, 1351814400, 1351814400, 1351814400, 1351814400, 
1351814400), class = c("POSIXct", "POSIXt"), tzone = "UTC")), .Names = c("storeID", "Time", "Date"), class = "data.frame", row.names = c(NA, 
-20L))

[编辑] 商店全天候开放。现在我希望有一个解决方案/方式将每个访问/行分配给一天中的24小时时段之一(即,09.00-10.00为1,10.00-11.00为2,等等)。那么我想连续两天每小时的访客人数。我希望能够将某些固定因素分开,例如storeID和City(本例中未显示)。 此外,如果没有访问者进入商店,我希望数据文件显示在此时间间隔内没有访问者,在这种情况下应返回0)。 的 [编辑]

请注意,我的数据文件很大,行数超过700k。

我希望我明白我的问题。

MvZB

2 个答案:

答案 0 :(得分:2)

First method:使用zooas illustrated here very nicely by Dirk。我已经解释了内联代码。这样的事情应该这样做:

df <- data # I just prefer `df` to `data`
df$storeID <- as.numeric(as.character(df$storeID)) # make sure its numeric
# instantiate the zoo object by providing values corresponding to time
require(zoo)
z <- zoo(as.numeric(as.character(df$storeID)), 
              as.POSIXct(paste(df$Date, df$Time)))
# create output data.frame with all possible timings
open_time <- paste(9:18, "00", "00", sep=":")
open_date <- as.character(unique(df$Date))
out.df <- data.frame(Date = rep(open_date, each=length(open_time)-1), 
                     Start = rep(head(open_time, -1), length(open_date)), 
                     End = rep(tail(open_time, -1), length(open_date)))
# Pointer for matching later
out.df$Pointer <- as.POSIXct(paste(out.df$Date, out.df$Start))
# initialise count to 0
out.df$count <- 0

# aggregate using zoo's magic function!
# the first part contains the storeID and is aggregated by
# the second column which creates hourly interval from the times in z (your data)
# and the third column sums up all values that fall in each hourly interval
agg.out <- aggregate(z, time(z) - as.numeric(time(z)) %% 3600, length)

# once that is done, just match the corresponding times and place them rightly
m.idx <- match( out.df$Pointer, index(agg.out))
out.df$count[!is.na(m.idx)] <- agg.out[m.idx[!is.na(m.idx)]]
out.df <- subset(out.df, select=-c(Pointer))

# and you're done
> out.df
#          Date    Start      End count
# 1  2012-11-01  9:00:00 10:00:00     0
# 2  2012-11-01 10:00:00 11:00:00     0
# 3  2012-11-01 11:00:00 12:00:00     0
# 4  2012-11-01 12:00:00 13:00:00     3
# 5  2012-11-01 13:00:00 14:00:00     0
# 6  2012-11-01 14:00:00 15:00:00     1
# 7  2012-11-01 15:00:00 16:00:00     0
# 8  2012-11-01 16:00:00 17:00:00     1
# 9  2012-11-01 17:00:00 18:00:00     0
# 10 2012-11-02  9:00:00 10:00:00     0
# 11 2012-11-02 10:00:00 11:00:00     0
# 12 2012-11-02 11:00:00 12:00:00     0
# 13 2012-11-02 12:00:00 13:00:00     5
# 14 2012-11-02 13:00:00 14:00:00     4
# 15 2012-11-02 14:00:00 15:00:00     4
# 16 2012-11-02 15:00:00 16:00:00     1
# 17 2012-11-02 16:00:00 17:00:00     0
# 18 2012-11-02 17:00:00 18:00:00     1

Second Method:不使用zoodrawing idea from Dirk again here。但我使用data.table进行快速访问。再次查看内联注释以获得解释。

require(data.table)
df <- data # I prefer df than data
# create an id column containing only the hours
df$id <- as.numeric(as.POSIXlt(paste(df$Date, df$Time))$hour)
# convert Date to character
df$Date <- as.character(df$Date)

# load package, create input data.table with Date and id as keys
require(data.table)
dt.in <- data.table(df)
setkey(dt.in, "Date", "id")
# get the count of customers / hour / date 
dt.tmp <- dt.in[, .N, by=c("Date", "id")]

# create the output template data.table with Date, Start and End
open_time <- paste(9:18, "00", "00", sep=":")
open_date <- as.character(unique(df$Date))
dt.out <- data.table(Date = rep(open_date, each=length(open_time)-1), 
                     Start = rep(head(open_time, -1), length(open_date)), 
                     End = rep(tail(open_time, -1), length(open_date)))
# create the id again by extracting hour                     
dt.out[, id := as.numeric(as.POSIXlt(paste(Date, Start))$hour)]
setkey(dt.out, "Date", "id")

# merge the two data.tables to get your output
dt.out <- dt.tmp[dt.out, list(Start, End, N)]
dt.out[, id := NULL]

> dt.out
#           Date    Start      End  N
#  1: 2012-11-01  9:00:00 10:00:00 NA
#  2: 2012-11-01 10:00:00 11:00:00 NA
#  3: 2012-11-01 11:00:00 12:00:00 NA
#  4: 2012-11-01 12:00:00 13:00:00  3
#  5: 2012-11-01 13:00:00 14:00:00 NA
#  6: 2012-11-01 14:00:00 15:00:00  1
#  7: 2012-11-01 15:00:00 16:00:00 NA
#  8: 2012-11-01 16:00:00 17:00:00  1
#  9: 2012-11-01 17:00:00 18:00:00 NA
# 10: 2012-11-02  9:00:00 10:00:00 NA
# 11: 2012-11-02 10:00:00 11:00:00 NA
# 12: 2012-11-02 11:00:00 12:00:00 NA
# 13: 2012-11-02 12:00:00 13:00:00  5
# 14: 2012-11-02 13:00:00 14:00:00  4
# 15: 2012-11-02 14:00:00 15:00:00  4
# 16: 2012-11-02 15:00:00 16:00:00  1
# 17: 2012-11-02 16:00:00 17:00:00 NA
# 18: 2012-11-02 17:00:00 18:00:00  1

答案 1 :(得分:2)

这是一个使用lubridate和因子的简单解决方案:

library(lubridate)

# Create a single date time variable
dt <- ymd_hms(paste(data$Date, data$Time))
# Extract the day
data$day <- floor_date(dt, "day")
# Extract the hour, converting it into a factor, so we
# get all hours shown
data$hour <- factor(hour(dt), 9:18)

# Count up with table
as.data.frame(table(data[c("day", "hour")]))