将字符串分成单独的列 R
Posted
技术标签:
【中文标题】将字符串分成单独的列 R【英文标题】:Break Apart a String into Separate Columns R 【发布时间】:2022-01-19 15:57:14 【问题描述】:我正在尝试整理一些数据,这些数据全部包含在名为“game_info”的 1 列中作为字符串。该数据包含即将到来的大学篮球比赛数据,包括日期、时间、球队 ID、球队名称等。理想情况下,每一个都是自己的列。我曾尝试使用空格分隔符分隔,但效果不佳,因为有些团队,例如“Duke”,他们的名字有 1 个部分,而团队的名字有 2 到 3 个部分(密歇根州、南达科他州等) )。也有团队名称中带有“-”破折号。
这是我的数据:
df <- data.frame(list(
game_info = c(
"12/16 7:00 PM 751 Appalachian State 752 Duke",
"12/16 7:00 PM 753 Chicago State 754 Indiana-Purdue",
"12/16 8:00 PM 755 Texas-Arlington 756 Oral Roberts",
"12/16 10:00 PM 757 Dartmouth 758 Stanford"
)
))
期望的输出:
date time away_team_id away_team_name home_team_id home_team_name
12/16 7:00 PM 751 Appalachian State 752 Duke
12/16 7:00 PM 753 Chicago State 754 Indiana-Purdue
12/16 8:00 PM 755 Texas-Arlington 756 Oral Roberts
12/16 10:00 PM 757 Dartmouth 758 Stanford
@Jonny Phelps @doRemy
【问题讨论】:
是否有一组有限的团队名称。与其使用正则表达式或类似策略,我可能会以编程方式(grepl)用串联版本(例如 Oral_Roberts)替换两个措辞学校名称,然后再次重试您的原始策略。这取决于有多少个团队名称。 出于好奇,读取的数据是什么文件类型(.txt、.csv、.xlsx ...)以及使用哪个函数? @rg255 只是用来自vegasinsider.com/college-basketball/odds/las-vegas的 rvest 刮擦 您能发布用于执行此操作的代码吗?我想知道是否有办法主动处理它而不是被动处理问题 - 我无法访问该网站,因为我在工作计算机上,所以它被阻止了 我认为您应该删除其中一个标签并改为使用regex
标签。因为它更容易引起正则表达式天才的注意。
【参考方案1】:
这是一个正则表达式。有关正则表达式的解释,请参阅regex101 link
regex <- "^(\\d2\\/\\d2)\\s*(\\d1,2:\\d2\\s*(PM|AM))\\s*(\\d+)\\s*([^\\d.]+)(\\d+)\\s*([^\\d.]+)$"
data <- data.frame(game_info=
"12/16 7:00 PM 751 Appalachian State 752 Duke"
,"12/16 7:00 PM 753 Chicago State 754 Indiana-Purdue"
,"12/16 8:00 PM 755 Texas-Arlington 756 Oral Roberts"
,"12/16 10:00 AM 757 Dartmouth 758 Stanford"
)
library(stringr)
out <- do.call(rbind, str_match_all(data, regex))
out <- as.data.frame(out)
# remove full string & AM/PM
out$V1 <- NULL
out$V4 <- NULL
names(out) <- c("date", "time", "away_team_id", "away_team_name",
"home_team_id", "home_team_name")
# remove white space from end
out$away_team_name <- trimws(out$away_team_name)
out$home_team_name <- trimws(out$home_team_name)
out
说明:
^(\d2/\d2) - 以 2 位数字/2 位数字开头,如 12/16。 ^ 是一个起始锚点,() 用于表示我们要捕获该组以进行拔除
\s* - 我们的第一组和下一组之间有 0 个或多个空格
(\d1,2:\d2\s*(PM|AM)) - 需要 1 或 2 位数字:2 位数字,然后可能是空格和 PM 或 AM
\s*(\d+)\s* - 任意位数周围的空格,第一个 id
([^\d.]+) - 所有非数字字符。如果您的团队名称中有数字,这将下降。如果是这样,找到一些例子,我们可以改进它。空白随后被捕获,因此稍后使用 trimws 删除
(\d+)\s* - 第二个 id 和空格
([^\d.]+)$ - 最后是其他队名和结尾句锚
【讨论】:
我必须向我学习一些这个正则表达式。 +1 - 对使用它有什么好的指导建议吗? regexone.com 是我最喜欢的教程网站。我可以试着把它拆开一点 k 在底部添加了一个快速而肮脏的解释【参考方案2】:一种简单的方法是将dplyr
库中的extract
与正则表达式一起使用:
# Define the column names:
column_names <- c("date", "time", "away_team_id", "away_team_name", "home_team_id", "home_team_name")
# Define the regex expression:
regex_expr <- paste(
"([0-9]1,2[/][0-9]1,2)", # The date
"([0-9]1,2:[0-9]1,2 [A-Za-z]2)", # The time
"([0-9]+)", # The away team id
"([A-Za-z -]+)", # The away team name
"([0-9]+)", # The home team id
"([A-Za-z -]+)" # The home team name
)
# Extract the columns:
df %>% extract(col = game_info, into = column_names, regex = regex_expr)
【讨论】:
呈现正则表达式的更好方式,我会记下:) 谢谢!它在正则表达式中确实有一些限制,因为每个“组”都需要在()
中。例如,你不能写(AM|PM)
,因为这会被认为是另一个组(即列)。【参考方案3】:
您可以尝试此解决方案,该解决方案只需要与[:digit:]
进行简单的模式匹配。一个额外的要求是在开头有日期和时间,在数字 ID 之间有角色团队信息。此外,您可以在拆分列表 dspl
上使用 trimws
删除不需要的 TAB kbd> 或类似的。
数据
dat <- structure(list(game_info = c("12/16 7:00 PM 751 Appalachian State 752 Duke",
"12/16 7:00 PM 753 Chicago State 754 Indiana-Purdue", "12/16 8:00 PM 755 Texas-Arlington 756 Oral Roberts",
"12/16 10:00 PM 757 Dartmouth 758 Stanford")), class = "data.frame", row.names = c(NA,
-4L))
dspl <- strsplit( dat$game_info, " +" )
dat_tmp <- cbind( date=as.vector(sapply( dspl, function(x) x[1] )),
time=unlist( lapply( dspl, function(x) paste( x[2:3], collapse=" " ) ) ),
away_team_id=as.vector( sapply( dspl, function(x) x[4] ) ) )
data.frame( dat_tmp,
away_team_name=sapply( dspl, function(x)
paste(x[ tail( head( grep( "[[:digit:]]", x )[3]:grep( "[[:digit:]]", x )[4], -1 ), -1 ) ], collapse=" ") ),
home_team_id=sapply( dspl, function(x)
x[ max( grep( "[[:digit:]]", x ) )] ),
home_team_name=sapply( dspl, function(x)
paste( tail( x[ max( grep( "[[:digit:]]", x ) ):length(x)], -1), collapse=" " ) ) )
date time away_team_id away_team_name home_team_id home_team_name
1 12/16 7:00 PM 751 Appalachian State 752 Duke
2 12/16 7:00 PM 753 Chicago State 754 Indiana-Purdue
3 12/16 8:00 PM 755 Texas-Arlington 756 Oral Roberts
4 12/16 10:00 PM 757 Dartmouth 758 Stanford
【讨论】:
【参考方案4】:这是另一种方法:
library(dplyr)
library(stringr)
library(tidyr)
my_pattern <- "\\b((1[0-2]|0?[1-9]):([0-5][0-9]) ([AaPp][Mm]))"
df %>%
mutate(date = substr(game_info, 1,5),
time = str_extract(game_info, my_pattern),
helper = str_remove(game_info, my_pattern), .keep="unused") %>%
mutate(helper = str_squish(str_remove(helper, substr(helper, 1,5)))) %>%
separate(helper, c("away_team_id", "away_team_name"), sep = '\\s', remove = FALSE) %>%
mutate(home_team_id = str_extract_all(helper, '(\\d+)(?!.*\\d)'),
home_team_name = sub(".*\\s", "", helper), .keep="unused")
date time away_team_id away_team_name home_team_id home_team_name
1 12/16 7:00 PM 751 Appalachian 752 Duke
2 12/16 7:00 PM 753 Chicago 754 Indiana-Purdue
3 12/16 8:00 PM 755 Texas-Arlington 756 Roberts
4 12/16 10:00 PM 757 Dartmouth 758 Stanford
【讨论】:
【参考方案5】:你可以使用 unglue :
unglue::unglue_unnest(
df, game_info,
"date hour away_team_id=\\d+ away_team_name home_team_id=\\d+ home_team_name", convert = TRUE)
#> date hour away_team_id away_team_name home_team_id home_team_name
#> 1 12/16 7:00 PM 751 Appalachian State 752 Duke
#> 2 12/16 7:00 PM 753 Chicago State 754 Indiana-Purdue
#> 3 12/16 8:00 PM 755 Texas-Arlington 756 Oral Roberts
#> 4 12/16 10:00 PM 757 Dartmouth 758 Stanford
由reprex package (v2.0.1) 于 2021-12-17 创建
为了正确解析它,我们必须提供一些正则表达式信息,而 unglue 会“猜测”其余部分,如果我们只是告诉 unglue id 必须是数字就足够了。 away_team_name
等价于 away_team_name=.*?
。 convert = TRUE
会将 id 放在数字列而不是文本中。
【讨论】:
酷包,感谢分享!以上是关于将字符串分成单独的列 R的主要内容,如果未能解决你的问题,请参考以下文章