我有一个包含约300个观察值的数据框,每个数据框都与我想要分成其组成数字的数字代码相关联。代码变量是3或4位整数,按其最后一位数对齐,&所以我想要的输出看起来像这样:
code d4 d3 d2 d1
403 <NA> 4 0 3
5123 5 1 2 3
105 <NA> 1 0 5
虽然我可以看到很多方法使用strsplit
(基础R)或stringr::str_split
来划分代码,但我很难将这些操作应用到我的数据框中。
library(stringr)
as.integer(unlist(str_split(5123, ""))[1]) # returns 5, the first digit - correct
as.integer(rev(unlist(str_split(5123, "")))[1]) # returns 3, the last digit - correct
但似乎是合理的(对我来说)操作
libray(dplyr)
df <- data.frame(code = c(403, 5123, 105))
df <- df %>%
mutate(
last = as.integer(rev(unlist(str_split(df$code,"")))[4])
)
返回
> df
code last
1 403 3
2 5123 3
3 105 3
显然,我对数据框中如何处理列表和原子向量的操作缺乏了解......
然后我确信separate()
包中的extract()
或tidyr
函数会有所帮助。当然,tidyr::separate()
会产生所需的结果,如果代码作为字符串提供,每个数字前面都有一个前导空格:
library(tidyr)
dfsep <- data.frame(code = c(" 4 0 3", "5 1 2 3", " 1 0 5"))
dfsep <- dfsep %>%
separate(
code, c("d4", "d3", "d2", "d1"), fill = "right", remove = FALSE
)
dfsep
code d4 d3 d2 d1
1 4 0 3 4 0 3
2 5 1 2 3 5 1 2 3
3 1 0 5 1 0 5
但是连续的数字串不能以这种方式分割; tidyr::separate()
df <- data.frame(code = c(403, 5123, 105))
df <- df %>%
separate(
code, c("d4", "d3", "d2", "d1"), fill = "right", remove = FALSE
)
df
code d4 d3 d2 d1
1 403 403 <NA> <NA> <NA>
2 5123 5123 <NA> <NA> <NA>
3 105 105 <NA> <NA> <NA>
虽然tidyr::extract()
的问题在于虽然它精美地提取了数字,但我还是找不到一组处理3和&amp; 4位整数:
dfext <- data.frame(code = c(403, 5123, 105))
dfext <- dfext %>%
extract(
code, c("d4", "d3", "d2", "d1"), "(.)(.)(.)(.)", remove = FALSE
)
dfext
code d4 d3 d2 d1
1 403 <NA> <NA> <NA> <NA>
2 5123 5 1 2 3
3 105 <NA> <NA> <NA> <NA>
也许我还没有理解如何为我的目的构建正确的正则表达式代码......
我已经查看了有关StackOverflow的相关问题,包括关于separate()和extract()的这个问题,但我看不出如何将答案应用于我自己的问题。问题here为具有固定长度值而非变量值的变量提供了解决方案。
非常感谢任何帮助,提示或观察!
P.S。为了给出背景,这是潜水比赛中潜水的数据框架。每行代表一次潜水,一次观察有多个分组变量:名称,年龄,性别,潜水数量(例如5个中的1个),董事会身高,潜水代码,潜水位置,关税,J1奖励,J2奖励,... J5奖,总奖(最高奖和最低奖),&amp;得分(总奖金乘以关税)。代码由FINA
确定答案 0 :(得分:1)
我们可以在使用stri_list2matrix
stringi
中的strsplit
n <- max(nchar(df$code)) #get the maximum number of characters
fmt <- paste0('%', n, 'd') #create a format for the `sprintf`
library(stringi)
#the list output from `strsplit` can be coerced to `matrix` using
#stri_list2matrix.
d1 <- stri_list2matrix(strsplit(sprintf( fmt, df$code), ''), byrow=TRUE)
#But, the output is character class, which we can convert to 'numeric'
m1 <- matrix(as.numeric(d1), ncol=ncol(d1), nrow=nrow(d1))
m1
# [,1] [,2] [,3] [,4]
#[1,] NA 4 0 3
#[2,] 5 1 2 3
#[3,] NA 1 0 5
对于&#39; dfsep&#39;数据集
v1 <- gsub('\\s+', '', dfsep$code)
n <- max(nchar(v1))
fmt <- paste0('%', n, 's')
d1 <- stri_list2matrix(strsplit(sprintf(fmt, v1), ''), byrow=TRUE)
m1 <- matrix(as.numeric(d1), ncol=ncol(d1), nrow=nrow(d1))
m1
# [,1] [,2] [,3] [,4]
#[1,] NA 4 0 3
#[2,] 5 1 2 3
#[3,] NA 1 0 5
我们可以cbind
使用原始数据集
cbind(dfsep, m1)
这可以作为应用于不同数据集的函数。
答案 1 :(得分:1)
仅测试了几个案例,但这也适用于不同类型的输入
f <- function(df) {
f <- tempfile()
df$ccode <- gsub('\\s+', '', df$code)
cat(file = f, sprintf('%4s', gsub('\\s+', '', df$ccode)), sep = "\n")
cbind(code = df$code, read.fwf(f, widths = rep(1, max(nchar(df$ccode)))))
}
df <- data.frame(code = c(403, 5123, 105))
f(df)
# code V1 V2 V3 V4
# 1 403 NA 4 0 3
# 2 5123 5 1 2 3
# 3 105 NA 1 0 5
dfsep <- data.frame(code = c(" 4 0 3", "5 1 2 3", " 1 0 5"))
f(dfsep)
# code V1 V2 V3 V4
# 1 4 0 3 NA 4 0 3
# 2 5 1 2 3 5 1 2 3
# 3 1 0 5 NA 1 0 5
答案 2 :(得分:1)
正则表达式应该是“(。)?(。)(。)(。)”
?项目用于零或一次
dfext %>% extract(code, c('d1','d2','d3','d4'), "(.)?(.)(.)(.)")
d1 d2 d3 d4
1 <NA> 4 0 3
2 5 1 2 3
3 <NA> 1 0 5
答案 3 :(得分:0)
一个简单的基础R解决方案
codes = c(403, 5123, 105)
# make all codes the same length
l = sapply(codes, nchar)
s = strrep(' ', max(l) - l)
new_codes = paste0(s, codes)
# split and combine into matrix
res = do.call(rbind, strsplit(new_codes, ''))
根据需要重新格式化:
res = data.frame(code=codes, res)
colnames(res) = c('code', 'd4', 'd3', 'd2', 'd1')
输出:
code d4 d3 d2 d1
1 403 4 0 3
2 5123 5 1 2 3
3 105 1 0 5