R 在每列中按分隔符排列多列,将字符串与值匹配
R Multiple Columns by Delimiter in each Column, Match String to Value
抱歉这个菜鸟问题,但几天后我还没弄清楚如何做到这一点。我一直在尝试使用R。简单地说,我有两列如下
A:B:C:D:F | 1.1:2.1:3.1:4.1:6.1
A:B:D:F | 1.2:2.2:4.2:6.2
A:B:C:F | 1.3:2.3:3.3:6.3
B:C:D:F | 2.4:3.4:4.4:6.4
请注意分隔符是“:”。最后我想要这个:
A | B | C | D | E | F
1.1 | 2.1 | 3.1 | 4.1 | NA | 6.1
1.2 | 2.2 | NA | 4.2 | NA | 6.2
1.3 | 2.3 | 3.3 | NA | NA | 6.3
NA | 2.4 | 3.4 | 4.4 | NA | 6.4
为什么我解决不了:
每行第二列的值不同,所以我需要循环检查第1列第x行是否有字符串并将数字插入对应的第2列第x行如果它存在。 **我刚刚为行选择了 1.1、1.2 等,以便于概念化。
0 或 NA 未包含在第 1 列中,因此当存在缺失值时我需要跳过列,例如在玩具示例中第 2 行缺少 C 和 E 建议列。每行没有所需数量的字符串和对应的值(第 1 行有 5 个字符串,第 2 到 4 行有 4
我会想象类似下面的东西,将“1”替换为相应的第 2 列,第 x 行的值,但我不知道该怎么做。我尝试过的另一种方法是在插入第 2 列值时遇到困难,即仅根据是否存在字符串来创建包含 1 和 0 的列,这就是我遇到代码片段的方式。
df$A <- ifelse(grepl("A", df$PASS, ignore.case = T), "1", "0")
抱歉写了这么长的文章,但我实在是卡住了。我觉得这超出了我的初学者水平 R。向任何可以解决这个问题的人致敬!
我们可以试试tidyverse
library(tidyverse)
res <- df1 %>%
map(~strsplit(., ":")) %>%
transpose %>%
map(~set_names(as.data.frame.list(as.numeric(.[[2]])), .[[1]])) %>%
bind_rows %>%
right_join(as.data.frame(setNames(rep(list(NA), 6), LETTERS[1:6])), .)
res
# A B C D E F
#1 1.1 2.1 3.1 4.1 NA 6.1
#2 1.2 2.2 NA 4.2 NA 6.2
#3 1.3 2.3 3.3 NA NA 6.3
#4 NA 2.4 3.4 4.4 NA 6.4
数据
df1 <- structure(list(col1 = c("A:B:C:D:F", "A:B:D:F", "A:B:C:F", "B:C:D:F"
), col2 = c("1.1:2.1:3.1:4.1:6.1", "1.2:2.2:4.2:6.2", "1.3:2.3:3.3:6.3",
"2.4:3.4:4.4:6.4")), .Names = c("col1", "col2"), class = "data.frame",
row.names = c(NA, -4L))
这里有一个只使用基础 R 的解决方案,没有 tidyverse 的魔力。它假定您可以将所有数据作为一个大字符串读入,但为输入流更改它并不难。
x <- "A:B:C:D:F | 1.1:2.1:3.1:4.1:6.1
A:B:D:F | 1.2:2.2:4.2:6.2
A:B:C:F | 1.3:2.3:3.3:6.3
B:C:D:F | 2.4:3.4:4.4:6.4"
data <- unlist(str_split(x, "\n"))
result <- matrix(as.numeric(NA), nrow = length(data), ncol = 6)
colnames(result) <- c("A", "B", "C", "D", "E", "F")
for (i in 1:length(data)) {
split_data <- unlist(str_split(data[i], " [|] "))
print(split_data)
indices <- unlist(str_split(split_data[1], ":"))
values <- unlist(str_split(split_data[2], ":"))
for (j in 1:length(indices)) {
result[i, indices[j]] <- as.numeric(values[j])
}
}
result
使用 base R 并以通用格式编写:
使用数据
df1 <- structure(list(col1 = c("A:B:C:D:F", "A:B:D:F", "A:B:C:F", "B:C:D:F"), col2 = c
("1.1:2.1:3.1:4.1:6.1", "1.2:2.2:4.2:6.2", "1.3:2.3:3.3:6.3", "2.4:3.4:4.4:6.4")),
.Names = c("col1", "col2"), class = "data.frame", row.names = c(NA, -4L))
那么代码就是
a=apply(df1,1,strsplit,":",fixed=TRUE)
b=lapply(a,function(x){y=`names<-`(as.numeric(x[[2]]),x[[1]]);data.frame(t(y))})
d=Reduce(function(x,y) merge(x,y,by =intersect(names(x),names(y)),all.x = TRUE,all.y = TRUE),b)
b1=max(match(names(unlist(b)),LETTERS))
e=LETTERS[1:b1][!LETTERS[1:b1]%in%names(d)]
f=`names<-`(data.frame(d,rep(list(NA),length(e))),c(names(d),e))
g=f[,order(names(f))]
Reduce(rbind,c(paste0(names(g),collapse = " | "),apply(g,1,paste0,collapse="|")))
如果数据是
x <- "A:B:C:D:F | 1.1:2.1:3.1:4.1:6.1
A:B:D:F | 1.2:2.2:4.2:6.2
A:B:C:F | 1.3:2.3:3.3:6.3
B:C:D:F | 2.4:3.4:4.4:6.4"
那么你只需要用下面的一行代码和上面的代码 运行 来转换它。
df1=matrix(unlist(strsplit(unlist(strsplit(x,"\n"))," | ",fixed = T)),ncol=2,byrow=T)
我建议查看我的 "splitstackshape" 包中的 cSplit
与 dcast
:
的组合
library(splitstackshape)
dcast(cSplit(as.data.table(df1)[, id := 1:nrow(df1)], names(df1), ":", "long"),
id ~ col1, value.var = "col2")
id A B C D F
1: 1 1.1 2.1 3.1 4.1 6.1
2: 2 1.2 2.2 NA 4.2 6.2
3: 3 1.3 2.3 3.3 NA 6.3
4: 4 NA 2.4 3.4 4.4 6.4
抱歉这个菜鸟问题,但几天后我还没弄清楚如何做到这一点。我一直在尝试使用R。简单地说,我有两列如下
A:B:C:D:F | 1.1:2.1:3.1:4.1:6.1
A:B:D:F | 1.2:2.2:4.2:6.2
A:B:C:F | 1.3:2.3:3.3:6.3
B:C:D:F | 2.4:3.4:4.4:6.4
请注意分隔符是“:”。最后我想要这个:
A | B | C | D | E | F
1.1 | 2.1 | 3.1 | 4.1 | NA | 6.1
1.2 | 2.2 | NA | 4.2 | NA | 6.2
1.3 | 2.3 | 3.3 | NA | NA | 6.3
NA | 2.4 | 3.4 | 4.4 | NA | 6.4
为什么我解决不了:
每行第二列的值不同,所以我需要循环检查第1列第x行是否有字符串并将数字插入对应的第2列第x行如果它存在。 **我刚刚为行选择了 1.1、1.2 等,以便于概念化。
0 或 NA 未包含在第 1 列中,因此当存在缺失值时我需要跳过列,例如在玩具示例中第 2 行缺少 C 和 E 建议列。每行没有所需数量的字符串和对应的值(第 1 行有 5 个字符串,第 2 到 4 行有 4
我会想象类似下面的东西,将“1”替换为相应的第 2 列,第 x 行的值,但我不知道该怎么做。我尝试过的另一种方法是在插入第 2 列值时遇到困难,即仅根据是否存在字符串来创建包含 1 和 0 的列,这就是我遇到代码片段的方式。
df$A <- ifelse(grepl("A", df$PASS, ignore.case = T), "1", "0")
抱歉写了这么长的文章,但我实在是卡住了。我觉得这超出了我的初学者水平 R。向任何可以解决这个问题的人致敬!
我们可以试试tidyverse
library(tidyverse)
res <- df1 %>%
map(~strsplit(., ":")) %>%
transpose %>%
map(~set_names(as.data.frame.list(as.numeric(.[[2]])), .[[1]])) %>%
bind_rows %>%
right_join(as.data.frame(setNames(rep(list(NA), 6), LETTERS[1:6])), .)
res
# A B C D E F
#1 1.1 2.1 3.1 4.1 NA 6.1
#2 1.2 2.2 NA 4.2 NA 6.2
#3 1.3 2.3 3.3 NA NA 6.3
#4 NA 2.4 3.4 4.4 NA 6.4
数据
df1 <- structure(list(col1 = c("A:B:C:D:F", "A:B:D:F", "A:B:C:F", "B:C:D:F"
), col2 = c("1.1:2.1:3.1:4.1:6.1", "1.2:2.2:4.2:6.2", "1.3:2.3:3.3:6.3",
"2.4:3.4:4.4:6.4")), .Names = c("col1", "col2"), class = "data.frame",
row.names = c(NA, -4L))
这里有一个只使用基础 R 的解决方案,没有 tidyverse 的魔力。它假定您可以将所有数据作为一个大字符串读入,但为输入流更改它并不难。
x <- "A:B:C:D:F | 1.1:2.1:3.1:4.1:6.1
A:B:D:F | 1.2:2.2:4.2:6.2
A:B:C:F | 1.3:2.3:3.3:6.3
B:C:D:F | 2.4:3.4:4.4:6.4"
data <- unlist(str_split(x, "\n"))
result <- matrix(as.numeric(NA), nrow = length(data), ncol = 6)
colnames(result) <- c("A", "B", "C", "D", "E", "F")
for (i in 1:length(data)) {
split_data <- unlist(str_split(data[i], " [|] "))
print(split_data)
indices <- unlist(str_split(split_data[1], ":"))
values <- unlist(str_split(split_data[2], ":"))
for (j in 1:length(indices)) {
result[i, indices[j]] <- as.numeric(values[j])
}
}
result
使用 base R 并以通用格式编写:
使用数据
df1 <- structure(list(col1 = c("A:B:C:D:F", "A:B:D:F", "A:B:C:F", "B:C:D:F"), col2 = c
("1.1:2.1:3.1:4.1:6.1", "1.2:2.2:4.2:6.2", "1.3:2.3:3.3:6.3", "2.4:3.4:4.4:6.4")),
.Names = c("col1", "col2"), class = "data.frame", row.names = c(NA, -4L))
那么代码就是
a=apply(df1,1,strsplit,":",fixed=TRUE)
b=lapply(a,function(x){y=`names<-`(as.numeric(x[[2]]),x[[1]]);data.frame(t(y))})
d=Reduce(function(x,y) merge(x,y,by =intersect(names(x),names(y)),all.x = TRUE,all.y = TRUE),b)
b1=max(match(names(unlist(b)),LETTERS))
e=LETTERS[1:b1][!LETTERS[1:b1]%in%names(d)]
f=`names<-`(data.frame(d,rep(list(NA),length(e))),c(names(d),e))
g=f[,order(names(f))]
Reduce(rbind,c(paste0(names(g),collapse = " | "),apply(g,1,paste0,collapse="|")))
如果数据是
x <- "A:B:C:D:F | 1.1:2.1:3.1:4.1:6.1
A:B:D:F | 1.2:2.2:4.2:6.2
A:B:C:F | 1.3:2.3:3.3:6.3
B:C:D:F | 2.4:3.4:4.4:6.4"
那么你只需要用下面的一行代码和上面的代码 运行 来转换它。
df1=matrix(unlist(strsplit(unlist(strsplit(x,"\n"))," | ",fixed = T)),ncol=2,byrow=T)
我建议查看我的 "splitstackshape" 包中的 cSplit
与 dcast
:
library(splitstackshape)
dcast(cSplit(as.data.table(df1)[, id := 1:nrow(df1)], names(df1), ":", "long"),
id ~ col1, value.var = "col2")
id A B C D F
1: 1 1.1 2.1 3.1 4.1 6.1
2: 2 1.2 2.2 NA 4.2 6.2
3: 3 1.3 2.3 3.3 NA 6.3
4: 4 NA 2.4 3.4 4.4 6.4