问题描述
我正在解决无法解决的问题!我必须要建立数据帧(“ Master”和“ Hours”)。“ Master” df有很多列,但具体如下:
大师
StoreNumber ... MON TUE WED THU FRI SAT SUN
1 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
...
注意:Master df在一周的几天内在StoreNumber之间有许多列,并保存大量数据(约3000家商店)
小时
BranchNumber Day TimeDiff
1 MON 7.50
1 TUE 6.00
1 WED 8.50
1 THU 2.00
1 FRI 1.00
1 SAT 2.50
3 MON 7.50
3 TUE 6.00
3 WED 8.50
3 THU 2.00
3 FRI 1.00
3 SAT 2.50
3 SUN 5.00
...
因此,我的想法是我试图将“小时”品牌编号与“主”商店编号匹配。一旦存在匹配项,它将“小时”表中的“天”列与“主”表中的星期几相匹配...它将针对每一行执行此操作,然后用相应的值填充星期几在“ TimeDiff”列中...如果商店和分支的编号不匹配(例如StoreNumber 2),则应跳过该行并移至下一行。另一个条件,例如BranchNumber'1',没有SUNDAY的数据,因此在“ Master”表中,SUNDAY单元格应保留为0 ...这应该适用于一周的任何一天。
输出应为“主”表,但应包含“小时”表中一周中所有天的数据。在此示例中,它应类似于:
StoreNumber ... MON TUE WED THU FRI SAT SUN
1 7.50 6.00 8.50 2.00 1.00 2.50 0
2 0 0 0 0 0 0 0
3 7.50 6.00 8.50 2.00 1.00 2.50 5.00
...
我尝试过的代码无法正常工作,但是我不确定它是否正确。我得到的最大问题是,它复制了第一行期望的行。例如,输出看起来像这样。
StoreNumber
1
2
2
3
3
4
4
5
5
5
全部都是重复的,并且有三倍,每87列都是相同的...但是,重复行的星期几全为0。
merged <- Master %>% select(-c("MON","TUE","WED","THU","FRI","SAT","SUN")) %>%
left_join(
Hours %>% pivot_wider(names_from = Day,values_from = TimeDiff),by = c('StoreNumber' = 'BranchNumber'))
merged <- merged %>% replace(is.na(.),0)
很抱歉,这个问题很长一段时间以来一直困扰着我,所以任何帮助/建议都将不胜感激
解决方法
如果我理解正确,则Master
表中有许多列,只有MON
至SUN
列需要更新。
这里有两种使用data.table
的功能来更新联接的方法。通过引用仅修改相关列,即不复制整个数据对象。这样可以避免前后调整Master
表的形状(或旋转)。
变种1
library(data.table)
days <- names(Master)[which(names(Master) == "MON") + (0:6)]
setDT(Master)[,(days) := lapply(.SD,as.double),.SDcols = days]
for (d in days) {
Master[Hours,on =.(StoreNumber = BranchNumber),(d) := TimeDiff[d == Day],by = .EACHI]
}
Master[]
StoreNumber OtherCol MON TUE WED THU FRI SAT SUN 1: 1 a 7.5 6 8.5 2 1 2.5 0 2: 2 b 0.0 0 0.0 0 0 0.0 0 3: 3 c 7.5 6 8.5 2 1 2.5 5
说明
-
days
包含各列的名称。days <- names(Master)[which(names(Master) == "MON") + (0:6)]
等效于days <- c("MON","TUE","WED","THU","FRI","SAT","SUN")
-
data.table
在更新列的某些部分时需要一致的数据类型。Master
中的天列被初始化为整数零,而TimeDiff
中的Hours
是数字。因此,Master
中的天列在更新前被强制加倍。 -
for
循环遍历每一天列,并为此列执行 update join 。对于每场比赛(by = .EACHI
),都会选择相关日期的Timediff
。
为了验证Master
是否已复制 ,我们可以致电
data.table::address(Master)
操作前后:Master
的地址未更改。
变种2
这种方法比较精简。它还使用了 update join ,但与变体1不同,因为它从长到宽格式重塑(或旋转)Hours
并从Master
中删除了天列而不是强制将一堆整数零键入数字:
library(data.table)
days <- c("MON","SUN")
Hours_wide <- dcast(setDT(Hours)[,Day := ordered(Day,levels = days)],BranchNumber ~ Day)
setDT(Master)[,(days) := NULL][
Hours_wide,on = .(StoreNumber = BranchNumber),(days) := mget(paste0("i.",days))]
Master[]
StoreNumber OtherCol MON TUE WED THU FRI SAT SUN 1: 1 a 7.5 6 8.5 2 1 2.5 NA 2: 2 b NA NA NA NA NA NA NA 3: 3 c 7.5 6 8.5 2 1 2.5 5
请注意,丢失的元素现在已初始化为NA
,由IMHO易于检测。如果需要,可以通过以下方式将NA
转换为另一个数值
Master[,nafill,fill = 0),.SDcols = days][]
StoreNumber OtherCol MON TUE WED THU FRI SAT SUN 1: 1 a 7.5 6 8.5 2 1 2.5 0 2: 2 b 0.0 0 0.0 0 0 0.0 0 3: 3 c 7.5 6 8.5 2 1 2.5 5
此方法使用mget(paste0("i.",days))
从Hours
中选择天数列。如果联接中两个data.tables中都有相同名称的列,则可以通过在列名称前分别加上x.
和i.
来区分列。因此,x.MON
引用第一个data.table中的MON
列,在这种情况下为Master
,而i.MON
引用第二个data.table中的MON
列{。{1}}的data.table。 Hours_wide
将列名称作为字符串,并返回各个列的值的列表。
数据
mget()
,
根据@GregorThomas的评论,这是一种更长,更广泛的方法:
master <- data.frame(
StoreNumber = 1:3,MON = 0,TUE = 0,WED = 0,THU = 0,FRI = 0,SAT = 0,SUN = 0
)
hours <- read.table(text = "BranchNumber Day TimeDiff
1 MON 7.50
1 TUE 6.00
1 WED 8.50
1 THU 2.00
1 FRI 1.00
1 SAT 2.50
3 MON 7.50
3 TUE 6.00
3 WED 8.50
3 THU 2.00
3 FRI 1.00
3 SAT 2.50
3 SUN 5.00",header = TRUE)
library(dplyr)
library(tidyr)
master %>%
pivot_longer(
cols = MON:SUN,names_to = "Day",values_to = "Time"
) %>%
left_join(hours,by = c("StoreNumber" = "BranchNumber","Day")) %>%
mutate(TimeDiff = replace_na(TimeDiff,0),Time = TimeDiff) %>%
select(-TimeDiff) %>%
pivot_wider(
id_cols = StoreNumber,names_from = Day,values_from = Time
)
# A tibble: 3 x 8
StoreNumber MON TUE WED THU FRI SAT SUN
<int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 7.5 6 8.5 2 1 2.5 0
2 2 0 0 0 0 0 0 0
3 3 7.5 6 8.5 2 1 2.5 5
编辑
这里是一个版本,其中master
具有其他列,并且存储了输出:
master <- data.frame(
StoreNumber = 1:3,other_colum = c("A","B","C"),header = TRUE)
library(dplyr)
library(tidyr)
master <- master %>%
pivot_longer(
cols = MON:SUN,Time = TimeDiff) %>%
select(-TimeDiff) %>%
pivot_wider(
names_from = Day,values_from = Time
)
master
# A tibble: 3 x 9
StoreNumber other_colum MON TUE WED THU FRI SAT SUN
<int> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 A 7.5 6 8.5 2 1 2.5 0
2 2 B 0 0 0 0 0 0 0
3 3 C 7.5 6 8.5 2 1 2.5 5