Использование strsplit, когда требуемый разделенный вектор символов не соответствует всем наблюдениям в переменной (R)

У меня есть данные, которые выглядят следующим образом:

   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: 10, ID: 96 и т. д.) - дата (M/D/Y) - идентификатор (S или T)

Я хочу разделить эту информацию и извлечь идентификационный номер (10 или 96), дату (например, 08.06.13) и идентификатор (S или T).

Для этого я попробовал следующее, используя strsplit:

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

Я думал, что это будет работать, как и в моих реальных данных, у меня> 130 000 наблюдений, и я не осознавал, что в некоторых наблюдениях была проблема, из-за которой в идентификаторе не было пробела " " между "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 с отдельными столбцами для идентификатора, даты, идентификатора, это было бы здорово.

редактировать: извините, забыл добавить данные для воспроизводимого примера:

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
                                                                                                                                                                                                                                                                              ))

person jalapic    schedule 03.07.2014    source источник


Ответы (2)


После того, как вы уволите этого человека, вводящего данные, я мог бы рассмотреть здесь регулярное выражение для сбора данных. Во-первых, вот только данные в столбце «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"

который работает до вашей последней новой строки неверных данных.

person MrFlick    schedule 03.07.2014
comment
Спасибо. Быстрый уточняющий вопрос. Некоторые из моих идентификаторов состоят из трех цифр. например У меня есть ID: 113 и ID: 120. Глядя на код регулярного выражения, нужно ли мне изменить его, чтобы решить эту проблему? - person jalapic; 03.07.2014
comment
@jalapic Нет \\d+ означает одну или несколько цифр, поэтому трехзначные идентификаторы вполне подходят. - person MrFlick; 03.07.2014
comment
это решение действительно хорошее, и оно хорошо работает. Однако в моих реальных данных это работает только до строки 99101, когда я сталкиваюсь с другой ошибкой ввода данных: переменная obs для следующих нескольких строк выглядит так: ID: 84DAY: 6/8/13 T следующий уникальный идентификатор после этого ID: 96 ДЕНЬ: 10.06.13 С примерно 400 строк спустя. Функция regcapturematches возвращает 96 6/10/13 S для строки 99102 (т.е. первой, где начинается id=84). Есть ли способ исправить это с помощью кода? или я должен вручную отредактировать ошибку «84DAY» с помощью gsub? (У меня больше не работает специалист по вводу данных!) - person jalapic; 03.07.2014
comment
Я не думаю, что мы можем исправить метод strsplit для таких данных, но вы можете изменить регулярное выражение на m<-regexpr("ID:\\s*(\\d+)\\s?DAY: (\\d+/\\d+/\\d+) (S|T)", obs, perl=T). Теперь это говорит о том, что перед DAY будет необязательный пробел вместо обязательного. - person MrFlick; 03.07.2014

Вы также можете использовать:

  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
person akrun    schedule 03.07.2014