将字符串分成单独的列 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的主要内容,如果未能解决你的问题,请参考以下文章

如何将日期时间分成单独的列? [复制]

使用tidyr将字符串长度不均匀的行拆分为R中的列[重复]

使用 Excel VBA 将字符串分成列

如何将字符串字典转换为字典并拆分为单独的列

MySQL查询将分隔字符串值拆分为单独的列[重复]

将字符串拆分为标记并将标记分成两个单独的数组