当需要的拆分字符向量对于变量 (R) 中的所有观测值不一致时使用 strsplit

Posted

技术标签:

【中文标题】当需要的拆分字符向量对于变量 (R) 中的所有观测值不一致时使用 strsplit【英文标题】:Using strsplit when required split character vector is not consistent for all observations in variable (R) 【发布时间】:2014-07-03 14:10:47 【问题描述】:

我的数据如下所示:

   duration                       obs   another
 1 1.801760     ID: 10 DAY: 6/10/13 S    orange
 2 1.868500     ID: 10 DAY: 6/10/13 S     green
 3 0.233562     ID: 10 DAY: 6/10/13 S    yellow
 4 5.538760       ID:96 DAY: 6/8/13 T    yellow
 5 3.436700       ID:96 DAY: 6/8/13 T      blue
 6 0.533856       ID:96 DAY: 6/8/13 T      pink
 7 2.302250       ID:96 DAY: 6/8/13 T    orange
 8 2.779420       ID:96 DAY: 6/8/13 T     green

我只包含了 3 个变量,但实际上我的数据有很多。我的问题与丑陋的“obs”变量有关。我从另一个人那里收到了这些数据,这些人在他们使用的软件中输入了不一致的信息。

'obs' 包含三个信息: - id(ID:10,ID:96等) - 日期(月/日/年) - 标识符(S 或 T)

我想拆分此信息并提取 ID 号(10 或 96)、日期(例如 2013 年 6 月 8 日)和标识符(S 或 T)。

为此,我使用 strsplit 尝试了以下操作:

temp<-strsplit(as.character(df$obs), " ")
mat<-matrix(unlist(temp), ncol=5, byrow=TRUE)

我认为这会在我的真实数据中起作用,我有超过 130,000 个观察结果,但我没有意识到某些观察结果存在问题,即 id 在“ID:”和数字之间没有空格“”。例如,在上面的数据中,“ID:96”在冒号和数字之间没有空格。显然,我收到了这条警告信息:

Warning message:
  In matrix(unlist(temp), ncol = 5, byrow = TRUE) :
  data length [796454] is not a sub-multiple or multiple of the number of rows [159291]

显然,strsplit 不能被强制转换为漂亮的常规列,因为 strsplit 的输出有两种形式:

[1] "ID:"     "10"      "DAY:"    "6/10/13" "S"   #when there is whitespace
[1] "ID:96"  "DAY:"   "6/8/13" "T"   #when there isn't whitespace

为了解决这个问题,我这样做了,我想如果我可以在“ID:”之后引入任何空格,它就可以工作:

df$obs <- gsub("ID:", "ID: ", df$obs)

但是当我执行 strsplit 时,这并没有起作用,它会将双空格识别为拆分数据的两个位置。

如果有人知道多个 strsplits 的解决方案,然后可以将其强制返回原始 df,并为 idnumber、date、identifier 单独列,那就太好了。

编辑:抱歉,忘记添加可重现示例的数据:

df<-structure(list(duration = c(1.80176, 1.8685, 0.233562, 5.53876, 
                        3.4367, 0.533856, 2.30225, 2.77942), obs = structure(c(1L, 1L, 
                                                                               1L, 2L, 2L, 2L, 2L, 2L), .Label = c("ID: 10 DAY: 6/10/13 S", 
                                                                                                                   "ID:96 DAY: 6/8/13 T"), class = "factor"), another = structure(c(3L, 
                                                                                                                                                                                    2L, 5L, 5L, 1L, 4L, 3L, 2L), .Label = c("blue", "green", "orange", 
                                                                                                                                                                                                                            "pink", "yellow"), class = "factor")), .Names = c("duration", 
                                                                                                                                                                                                                                                                              "obs", "another"), class = "data.frame", row.names = c(NA, -8L
                                                                                                                                                                                                                                                                              ))

【问题讨论】:

【参考方案1】:

在您解雇那个数据输入人员后,我可能会考虑在这里使用正则表达式来捕获数据。首先,这只是“obs”列中的数据(在您的评论中添加附加值)

obs<-c("ID: 10 DAY: 6/10/13 S", "ID: 10 DAY: 6/10/13 S", "ID: 10 DAY: 6/10/13 S", 
"ID:96 DAY: 6/8/13 T", "ID:96 DAY: 6/8/13 T", "ID:96 DAY: 6/8/13 T", 
"ID:96 DAY: 6/8/13 T", "ID:96 DAY: 6/8/13 T", "ID: 84DAY: 6/8/13 T")

接下来,我可以使用

m<-regexpr("ID:\\s*(\\d+) ?DAY: (\\d+/\\d+/\\d+) (S|T)", obs, perl=T)

接下来,我使用辅助函数 regcapturedmatches() 来提取捕获的匹配项(它的工作原理类似于 regmatches(),但用于捕获组)

do.call(rbind, regcapturedmatches(obs,m))

#      [,1] [,2]      [,3]
# [1,] "10" "6/10/13" "S" 
# [2,] "10" "6/10/13" "S" 
# [3,] "10" "6/10/13" "S" 
# [4,] "96" "6/8/13"  "T" 
# [5,] "96" "6/8/13"  "T" 
# [6,] "96" "6/8/13"  "T" 
# [7,] "96" "6/8/13"  "T" 
# [8,] "96" "6/8/13"  "T" 
# [9,] "84" "6/8/13"  "T"

这将返回一个值矩阵。然后,您可以随意处理这些字符值。您可以将它们转换为正确的类并附加到您的 data.frame。

但是,如果您确实想使用 strsplit,您可以使用“:”或带有“:”前面的选项的空格来分割

do.call(rbind, strsplit(obs,"(:|:?\\s+)", obs))

#      [,1] [,2]    [,3]     [,4]      [,5]
# [1,] "ID" "10"    "DAY"    "6/10/13" "S" 
# [2,] "ID" "10"    "DAY"    "6/10/13" "S" 
# [3,] "ID" "10"    "DAY"    "6/10/13" "S" 
# [4,] "ID" "96"    "DAY"    "6/8/13"  "T" 
# [5,] "ID" "96"    "DAY"    "6/8/13"  "T" 
# [6,] "ID" "96"    "DAY"    "6/8/13"  "T" 
# [7,] "ID" "96"    "DAY"    "6/8/13"  "T" 
# [8,] "ID" "96"    "DAY"    "6/8/13"  "T" 
# [9,] "ID" "84DAY" "6/8/13" "T"       "ID"

直到您最新的新错误数据行为止。

【讨论】:

谢谢。一个快速澄清的问题。我的一些身份证是三位数。例如我有“ID:113”和“ID:120”。查看 regexpr 代码,我是否需要更改它来解决这个问题? @jalapic Nope \\d+ 表示一位或多位数字,所以三位数字 ID 就可以了。 这个解决方案非常好,而且效果很好。但是,在我的真实数据中,它只在第 99101 行之前有效,当我遇到另一个数据输入错误时:接下来几行的“obs”变量如下所示:“ID: 84DAY: 6/8/13 T”下一个唯一 ID之后是大约 400 行之后的“ID: 96 DAY: 6/10/13 S”。 regcapturematches 函数为第 99102 行返回“96”“6/10/13”“S”(即第一个 id=84 开始的行)。有没有办法用代码解决这个问题?还是我应该使用 gsub 手动编辑“84DAY”错误? (数据输入人员不再为我工作!) 我不认为我们可以为这样的数据修复strsplit 方法,但您可以将正则表达式更改为m&lt;-regexpr("ID:\\s*(\\d+)\\s?DAY: (\\d+/\\d+/\\d+) (S|T)", obs, perl=T)。这现在表示“DAY”之前将有一个可选空格,而不是必需的。【参考方案2】:

你也可以使用:

  read.table(text=gsub(":"," ", df$obs),header=F,stringsAsFactors=F)
  V1 V2  V3      V4 V5
# 1 ID 10 DAY 6/10/13  S
# 2 ID 10 DAY 6/10/13  S
# 3 ID 10 DAY 6/10/13  S
# 4 ID 96 DAY  6/8/13  T
# 5 ID 96 DAY  6/8/13  T
# 6 ID 96 DAY  6/8/13  T
# 7 ID 96 DAY  6/8/13  T
# 8 ID 96 DAY  6/8/13  T

【讨论】:

以上是关于当需要的拆分字符向量对于变量 (R) 中的所有观测值不一致时使用 strsplit的主要内容,如果未能解决你的问题,请参考以下文章

R从字符向量输入名称到函数公式语句

当正则表达式的某些部分要保存在后续的分割字符串中时,如何使用正则表达式在R中拆分字符串?

R语言--字符串操作

第2章--创建数据集

R中时间序列数据的拆分应用聚合

将数据框列拆分为 R 中的向量