按组划分参考行的所有行

时间:2017-07-20 19:59:37

标签: r

以下是我正在使用的示例表:

n = c(rep("A",3),rep("B",3),rep("C",3))
m = c("X", "Y", "Z", "X", "Y", "Z", "X", "Y", "Z")
s = 1:9 
b = 5:13
c = 20:28
d = c(rep("abc", 9))
df = data.frame(d, n, m, s, b, c) 
df

下面是表格的样子:

d   n   m   s   b   c
abc A   X   1   5   20
abc A   Y   2   6   21
abc A   Z   3   7   22
abc B   X   4   8   23
abc B   Y   5   9   24
abc B   Z   6   10  25
abc C   X   7   11  26
abc C   Y   8   12  27
abc C   Z   9   13  28

我将每行称为其列n和m值的串联(例如AX行,CZ行等)我想将每行A行划分为AY行,每行为B行。 BY行的行,以及CY行的每个C行(可能不总是Y,有时是X或Z)。我基本上想要按组(其中列n是组)来重新定义数据(列s,b和c),使用X,Y或Z(列m)作为基础。

我需要列d,n和m保持不变。如果可能的话,我想通过直接在代码中引用X,Y或Z来表示哪一行是基数,而不是[1],[2]或[3](因为它们可能)并不总是以相同的顺序,并且对用户来说更直观)。我是R的新手,并使用dplyr,但我还没有找到一个好方法。

感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

使用data.table

library(data.table)

setDT(df)

divselect <- "Y"

set(df, j = "s", value = as.numeric(df[["s"]]))
set(df, j = "b", value = as.numeric(df[["b"]]))
set(df, j = "c", value = as.numeric(df[["c"]]))

set命令是为了避免错误。这些列目前为integer,但您将成为double。如果在您的真实世界示例中,它们已经double,则不需要这样做。

divselect的值会更改您用作基础的列行。您可以根据需要将其更改为XZ

df[, `:=`(s = s/s[m == divselect],
          b = b/b[m == divselect],
          c = c/c[m == divselect]),
   by = n]

结果:

#      d n m     s         b         c
# 1: abc A X 0.500 0.8333333 0.9523810
# 2: abc A Y 1.000 1.0000000 1.0000000
# 3: abc A Z 1.500 1.1666667 1.0476190
# 4: abc B X 0.800 0.8888889 0.9583333
# 5: abc B Y 1.000 1.0000000 1.0000000
# 6: abc B Z 1.200 1.1111111 1.0416667
# 7: abc C X 0.875 0.9166667 0.9629630
# 8: abc C Y 1.000 1.0000000 1.0000000
# 9: abc C Z 1.125 1.0833333 1.0370370

跟进

  

我有一个问题:有没有办法概括得到的列   重订基期?我希望这段代码能够处理额外的数字   列(超过3个没有专门调用每个)。即可   我将除法定义为除d,n和m之外的所有列?

是的,您可以在lapply内外使用data.table来完成此操作。

setDT(df)

divselect <- "Y"

funcnumeric <- function(x) {
  set(df, j = x, value = as.numeric(df[[x]]))
  NULL
}

modcols <- names(df)[!(names(df) %in% c("d", "n", "m"))]

a <- lapply(modcols, funcnumeric)

这将替换第一个答案中的三个set命令。我们使用lapply而不是指定每个列,而不是dnm。注意我返回NULL以避免凌乱的函数返回文本;因为这是data.table,所有这些都已完成。

funcdiv <- function(x, pos) {
  x/x[pos]
}

df[ , (modcols) := lapply(.SD, 
                          funcdiv, 
                          pos = which(m == divselect)), 
    by = n, 
    .SDcols = modcols]

这与以前略有不同。在这里,我们创建一个简单的函数,它将向量除以该向量的值a pos参数指定的位置。我们将其应用于.SD中的每一列,并将pos值作为m列等于divselect的值的位置传递,在这种情况下,它等于Y。由于我们正在指定by = n,因此pos中的每个值都将确定funcdiv的向量和n参数。参数.SDcols指定我们要lapply这个函数,它是我们分配给变量modcols的同一组列。我们将所有这些分配回modcols

结果:

#      d n m     s         b         c
# 1: abc A X 0.500 0.8333333 0.9523810
# 2: abc A Y 1.000 1.0000000 1.0000000
# 3: abc A Z 1.500 1.1666667 1.0476190
# 4: abc B X 0.800 0.8888889 0.9583333
# 5: abc B Y 1.000 1.0000000 1.0000000
# 6: abc B Z 1.200 1.1111111 1.0416667
# 7: abc C X 0.875 0.9166667 0.9629630
# 8: abc C Y 1.000 1.0000000 1.0000000
# 9: abc C Z 1.125 1.0833333 1.0370370 

答案 1 :(得分:0)

使用dplyr

df2 <- filter(df, m=="Y") %>% setNames(.,c("e","n","f","g","h","i"))
df1 <- full_join(df,df2,by="n") %>%
          mutate(s=s/g, b=b/h, c=c/i) %>%
          select(-c(e,f,g,h,i))

输出

    d n m     s         b         c
1 abc A X 0.500 0.8333333 0.9523810
2 abc A Y 1.000 1.0000000 1.0000000
3 abc A Z 1.500 1.1666667 1.0476190
4 abc B X 0.800 0.8888889 0.9583333
5 abc B Y 1.000 1.0000000 1.0000000
6 abc B Z 1.200 1.1111111 1.0416667
7 abc C X 0.875 0.9166667 0.9629630
8 abc C Y 1.000 1.0000000 1.0000000
9 abc C Z 1.125 1.0833333 1.0370370

答案 2 :(得分:0)

使用您的数据,我们可以构建一个要划分的行表,然后使用match

选择表行
table = df[which(df$m == "Y"), c(2,4:6)]
New_df = df
New_df[, 4:6] = New_df[,4:6]/table[match(df$n, table$n), 2:4]
New_df
    d n m     s         b         c
1 abc A X 0.500 0.8333333 0.9523810
2 abc A Y 1.000 1.0000000 1.0000000
3 abc A Z 1.500 1.1666667 1.0476190
4 abc B X 0.800 0.8888889 0.9583333
5 abc B Y 1.000 1.0000000 1.0000000
6 abc B Z 1.200 1.1111111 1.0416667
7 abc C X 0.875 0.9166667 0.9629630
8 abc C Y 1.000 1.0000000 1.0000000
9 abc C Z 1.125 1.0833333 1.0370370