在R中,按行重新排列数据框的列。

4

我在R中有一个数据框,看起来像这样:

df <- paste0(c(letters[seq( from = 1, to = 5 )]),":",round(runif(5),2)) 
df <- as.data.frame(t(df))
df2 <- paste0(c(letters[seq( from = 1, to = 5 )]),":",round(runif(5),2)) 
df2 <- as.data.frame(t(df2))
df3 <- paste0(c(letters[seq( from = 1, to = 5 )]),":",round(runif(5),2)) 
df3 <- as.data.frame(t(df3))
df <- rbind(df, setNames(sample(df2), names(df2)))
df <- rbind(df, setNames(sample(df3), names(df3)))

df


      V1     V2     V3     V4     V5
1 a:0.21 b:0.49 c:0.96 d:0.95 e:0.03
2 b:0.33 c:0.25 e:0.81 d:0.94 a:0.02
3 c:0.34 e:0.26 a:0.31 d:0.55 b:0.63

冒号前的字符反映了测量的类型,冒号后的数字反映了测量本身。

我想重新排列每一行,以便每种测量(即,冒号前的字符)在同一列中。 应该看起来像这样:

      V1     V2     V3     V4     V5
1 a:0.21 b:0.49 c:0.96 d:0.95 e:0.03
2 a:0.02 b:0.33 c:0.25 d:0.94 e:0.81 
3 a:0.31 b:0.63 c:0.34 d:0.55 e:0.26


或者更好的方法是:
  a    b    c    d      e
1 0.21 0.49 0.96 0.95 0.03
2 0.02 0.33 0.25 0.94 0.81 
3 0.31 0.63 0.34 0.55 0.26

有什么方法可以重新排列每行,使得在给定列中冒号前的字符相同?

非常感谢您提供的建议!

2个回答

3
你可以使用substr函数。
res <- as.data.frame(t(apply(df, 1, function(x) x[order(substr(x, 1, 1))])))
res
#       V1     V2     V3     V4     V5
# 1 a:0.96 b:0.94 c:0.34 d:0.85  e:0.2
# 2 a:0.84 b:0.32 c:0.78 d:0.67 e:0.32
# 3 a:0.59 b:0.82 c:0.79  d:0.7  e:0.2

或者,去掉后缀并转换为数字:

res <- as.data.frame(t(apply(df, 1, function(x) 
  as.numeric(as.character(substring(x, 3)[order(substr(x, 1, 1))])))))
res
#     V1   V2   V3   V4   V5
# 1 0.96 0.94 0.34 0.85 0.20
# 2 0.84 0.32 0.78 0.67 0.32
# 3 0.59 0.82 0.79 0.70 0.20

str(res)
# 'data.frame': 3 obs. of  5 variables:
# $ V1: num  0.96 0.84 0.59
# $ V2: num  0.94 0.32 0.82
# $ V3: num  0.34 0.78 0.79
# $ V4: num  0.85 0.67 0.7
# $ V5: num  0.2 0.32 0.2

或者,使用正则表达式:

as.data.frame(t(apply(df2, 1, function(x) {
  g1 <- gsub(x, pattern="(\\w+\\:).*", r="\\1")
  g2 <- gsub(x, pattern="\\w+\\:(.*)", r="\\1")
  as.numeric(as.character(g2[order(g1)]))
})))
#     V1   V2   V3   V4   V5
# 1 0.96 0.94 0.34 0.85  0.2
# 2 0.84 0.32 0.78 0.67 0.32
# 3 0.59 0.82 0.79  0.7  0.2

df2的数据

df2 <- df
df2[] <- lapply(df2, function(x) as.character(x))
df2[2, ] <- c("cc:0.78", "e:0.32", "dd:0.67", "a:0.84", "bb:0.32")
df2
#        V1     V2      V3     V4      V5
# 1  a:0.96 b:0.94  c:0.34 d:0.85   e:0.2
# 2 cc:0.78 e:0.32 dd:0.67 a:0.84 bb:0.32
# 3   d:0.7 b:0.82   e:0.2 a:0.59  c:0.79

太好了!有没有办法让substr选择冒号之前的所有字符,而不是定义起始和结束位置?在我的实际数据中,有时在冒号之前有多个字符。 - Farln Parmb

2
我们可以以长格式获取数据,将冒号前后的数据分别放入separate列中,并再次以宽格式获取数据。
library(dplyr)
library(tidyr)

df %>%
  mutate(row = row_number()) %>%
  pivot_longer(cols = -row, names_to = 'col') %>%
  separate(value, into = c('col', 'value'), sep = ":", convert = TRUE) %>%
  pivot_wider(names_from = col, values_from = value) %>%
  select(-row)

# A tibble: 3 x 5
#    a     b     c     d     e
#  <dbl> <dbl> <dbl> <dbl> <dbl>
#1  0.1   0.61  0.53  0.82  0.21
#2  0.62  0.93  0.18  0.39  0.34
#3  0.94  0.95  0.41  0.74  0.9 

非常好。您可以在pivot_longer函数中添加, names_to="col",这样就不需要后来取消选择name,就可以将其缩短一行。另外,使用into=c('name', 'value'),然后pivot_wider只需要指定id_cols列(行)即可。其他参数采用默认值。我知道您喜欢简洁的代码。 :) - Edward

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接