合并和左联接保持R 说明

问题描述

我正在解决无法解决的问题!我必须要建立数据帧(“ 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),则应跳过该行并移至下一行。另一个条件,例如Br​​anchNumber'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表中有许多列,只有MONSUN列需要更新。

这里有两种使用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