提问人:Honza88 提问时间:10/31/2023 最后编辑:Darren TsaiHonza88 更新时间:11/1/2023 访问量:89
按多个分隔符拆分列(保留它们)并分成不相等的列数
Spliting column by multiple delimiters (keeping them) and into unequal number of columns
问:
假设我有一个这样的数据帧(简化的,我的问题的类似版本):
ID <- c(1,2,3)
value <- c("1+4-3", "2+7-6+4-3", "-1+3")
df <- data.frame(ID, value)
ID value
1 1+4-3
2 2+7-6+4-3
3 -1+3
我需要通过多个分隔符( 和 )将列拆分为多列,同时将分隔符保留在单独的列中。value
+
-
生成的数据帧应如下所示:
ID x1 x2 x3 x4 x5 x6 x7 x8 x9
1 1 + 4 - 3 <NA> <NA> <NA> <NA>
2 2 + 7 - 6 + 4 - 3
3 - 1 + 3 <NA> <NA> <NA> <NA> <NA>
此外,我不知道我需要多少个结果列(可能不是示例中的 9 个,而是 50 个)。
实现这一目标的最佳方法是什么?
谢谢
答:
2赞
ThomasIsCoding
10/31/2023
#1
如果您的数字仅由digits
df %>%
mutate(value = str_extract_all(value, "\\d+|\\D")) %>%
unnest(value) %>%
mutate(name = seq_len(n()), .by = ID) %>%
pivot_wider(names_prefix = "X")
这给了
# A tibble: 2 × 10
ID X1 X2 X3 X4 X5 X6 X7 X8 X9
<dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 1 1 + 4 - 3 NA NA NA NA
2 2 2 + 7 - 6 + 4 - 3
1赞
deschen
10/31/2023
#2
您可以执行以下操作:
library(tidyverse)
df |>
separate_longer_delim(cols = value, delim = regex("(?=\\+|-)")) |>
separate_longer_position(cols = value, width = 1) |>
mutate(pos = row_number(), .by = ID) |>
pivot_wider(values_from = value,
names_from = "pos",
names_prefix = "X")
# A tibble: 3 × 10
ID X1 X2 X3 X4 X5 X6 X7 X8 X9
<int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 1 1 + 4 - 3 NA NA NA NA
2 2 2 + 7 - 6 + 4 - 3
3 3 - 1 + 3 NA NA NA NA NA
评论
2赞
PGSA
10/31/2023
请注意,这不会处理具有多个数字的数字,这些数字可能相关,也可能不相关。
1赞
Osdorp
10/31/2023
#3
[已编辑以处理具有多个数字的数字]
仅使用 r-base:
# Example data
ID <- c(1,2)
value <- c("1+4-3","2+72-6+42-3")
df <- data.frame(ID,value)
# Function to do custom split
mysplit <- function(x){
a <-gregexpr('[0-9]+',x)
b <- gregexpr('[+-]{1}',x)
res <- unlist(c(regmatches(x,a),regmatches(x,b)))
res[order(unlist(c(a,b)))]
}
# split and fill with NAs
s <- sapply(df$value,mysplit)
mlength <- max(sapply(s,length))
s <- sapply(s, function(x) c(x,rep(NA,mlength - length(x))))
# Return dataframe
data.frame(ID = df$ID,t(s))
ID X1 X2 X3 X4 X5 X6 X7 X8 X9
1+4-3 1 1 + 4 - 3 <NA> <NA> <NA> <NA>
2+72-6+42-3 2 2 + 72 - 6 + 42 - 3
编辑
评论
1赞
PGSA
10/31/2023
请注意,此解决方案无法处理具有多个数字的数字,如果这是相关的(OP 中不清楚)。
1赞
Osdorp
11/1/2023
我还没有意识到。现在我认为它有效
1赞
PGSA
10/31/2023
#4
我的方法:
ID <- c(1,2,3)
value <- c("1+4-3","2+7-6+4-3","25+110/2*214")
# added example 3 to show effect on numbers with >1 digit
df <- data.frame(ID,value)
df |> dplyr::mutate(
X = lapply(value, \(x) {
# split by word/nonword boundaries
y <- stringr::str_split(x, pattern = "\\b", simplify = TRUE)
# drop the empty first and last strings
y[nzchar(y)]
})) |> tidyr::unnest_wider(X, names_sep = "")
给
# A tibble: 3 × 11
ID value X1 X2 X3 X4 X5 X6 X7 X8 X9
<dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 1 1+4-3 1 + 4 - 3 NA NA NA NA
2 2 2+7-6+4-3 2 + 7 - 6 + 4 - 3
3 3 25+110/2*214 25 + 110 / 2 * 214 NA NA
如果你把管道拆下来,你会得到这个,IMO在某些方面可能更整洁:unnest_wider
ID value X
1 1 1+4-3 1, +, 4, -, 3
2 2 2+7-6+4-3 2, +, 7, -, 6, +, 4, -, 3
3 3 25+110/2*214 25, +, 110, /, 2, *, 214
4赞
Darren Tsai
10/31/2023
#5
您可以从以下位置使用:separate_wider_delim()
tidyr
library(tidyr)
df %>%
separate_wider_delim(value,
delim = stringr::regex("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)"),
too_few = "align_start",
names_sep = '',
names_repair = ~ sub("value", "X", .x))
# # A tibble: 3 × 10
# ID X1 X2 X3 X4 X5 X6 X7 X8 X9
# <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
# 1 1 1 + 4 - 3 NA NA NA NA
# 2 2 2 + 7 - 6 + 4 - 3
# 3 3 - 1 + 3 NA NA NA NA NA
评论
1赞
PGSA
10/31/2023
我认为这是迄今为止最干净的非基础答案。
评论