Работа с грязными датами

Надеюсь, вы не думали, что я прошу совета по вопросам отношений.

Редко, я должен предложить респондентам опроса возможность указать, когда произошло событие. Какие результаты - ужасно грязная строка, которую я честно просто не знаю, что делать. Помимо перекодировки вручную.

Здесь это короткий образец, из тысяч:

c("May2/ 12 noon", "9:45 am", "11:00 AM AST", "April 27 / 12:00 AST", 
"11:40 AM AST", "April 25 2011", "April 12th 2011 / 8:44", "April 12 2011 / 8:36am", 
"April 12 2011 / 8:30am", "April 12th 2011 / 8:18", "April 12 2011 / 8:12am", 
"April 11th 2011 / 5:57pm", "April 11th 2011 / 5:49pm", "April 11th 2011 / 5:42pm", 
"April 11th 2011 / 5:36pm", "April 11th 2011 / 5:27", "April 5 @ 11:26am", 
"8:50", "April 4th 12:45pm", "April 4th around 10am", "April 4th around 10am", 
"Mar 18, 2011 9:33am", "Mar 18, 2011 9:27am", "df", "fg", "12:16", 
"9:50", "Feb 8, 2011 / 12:20pm", "8:34 am  2/4/11", "Jan 31, 2011 2:50pm", 
"Jan 31, 2011 2:45pm", "Jan 31, 2011 2:38pm", "Jan 31, 2011 2:26pm", 
"11h09", "11:00 am", "1h02 pm", "10h03", "2h10", "Jan 13, 2011 9:50am Van", 
"Jan 12, 2011", "Jan 12, 2011 3:59pm", "Jan 12     14:19PM", 
"Jan 12, 2011 1:35pm", "Jan 12,2011 1:28pm", "1h36", "9h15", 
"9h09", "8h51", "8h45", "8h35", "1h12 pm", "12h59", "11h52 am", 
"10h45", "15h55", "Dec 31, 10 11:11am", "Dec 31,10 10:15am", 
"Dec 30, 2010 12:32pm", "Dec 30, 2010 12:18pm", "9:16 am", "11h16 am", 
"11h12", "9h29 am", "11h38", "Dec 16, 2010", "December 16, 2010", 
"December 16, 2010", "Dec 15,2010", "DEC 14 2010", "Dec 14 11:38", 
"Dec 14 11:35", "Dec 14 11:25", "December 13, 2010", "Dec 10, 1:38 pm", 
"Dec 10, 1:26 pm", "Dec 10, 1:20 pm", "Dec 10, 1:12 pm", "December 9 2010", 
"11h10 am", "10h59 am", "10:50 am", "Tues Dec 7th, 9:45 Van time", 
"Dec 3, 2010 12:30pm", "Dec 3, 2010 12:20pm", "Dec 3, 2010 12:10 pm", 
"November 30, 2010 4.02pm", "November 30, 2010", "november 29 120pm", 
"November 29 2010 11:27", "10:12am November 29, 2010", "Nov 26/10 1:18pm", 
"10:56 am", "Nov 24", "nov 24/ 4:20 PM AST", "Nov 24/4:00 PM AST", 
"NOVEMBER 24/10  2:10 pm", "November 24/10  11:00 a.m.", "12:05 MST", 
"3.55PM", "Nov. 17/10 12:45 pm", "Nov. 16/10  12:00 noon", "Nov. 16/10 11;50 a.m.", 
"nov 16/10  11:30 a.m.", "November 12, 2010 @ 12:23pm", "november 11 2010  2:20pm", 
"November 11 2010  2:15pm", "November 11 2:00pm", "Nov. 10/10:22am", 
"nov. 8/10...3:19 pm", "Nov 8/10  1;50 p.m.", "November 8/10...12 noon", 
"November 8/10..10: am", "Nov 5, 2010  1:10 pm", "11:32 am CST", 
"Nov 4  11:10", "nov 3 10am", "9:30 am", "11/02/2010 1:50PM", 
"Oct 29/10 2:50PM", "Oct 28 @ 11:20am", "27Oct10 10:40am", "10/26/2010 11:18", 
"Oct 26/10 11am", "Oct 26/10 10:30 am", "Oct 26 10:50", "10/25/2010 13:50", 
"10/22/2010  10:15", "Oct 22/10 10AM", "Oct 21, 2010 3:00 pm", 
"Oct 21, 2010 2:59", "10/21/2010 11:50", "10/21/2010 11:45", 
"10/21/2010 11:40", "10/21/2010 11:30", "11:30", "Oct 20 approx 1pm", 
"Oct 20/10 4:50PM", "13:48", "13:45", "Oct 20, 2010 11:45 am", 
"October 19th 3:05pm", "Oct 18,2010 2:15pm", "Oct 18/10 3:10PM", 
"10:30 am", "Oct 15/10 11:50am", "oct 14 @ 11:05am", "Oct 14/ 11:06", 
"4:40 oct 13 atlantic", "oct 13 4:05 pm atlantic", "oct 13 1:45 atlantic time", 
"Oct 13 / 10:37", "OCT 12 3:33", "Oct 12,2010 1:10pm", "Oct 12 / 11:45", 
"Oct 12 / 9:45", "Oct 8. 2010/ 2:00", "Oct 8/10- 1145am", "2 Sept 2010 3.52pm", 
"2 Sept 2010 10.21am", "1 Sept 2010 2.05pm", "1 Sept 2010", "31 Aug 2010 - 11.52am", 
"31 aug 10:40am", "31 aug 2010 - 10am")

Как правило, эти события происходят вблизи даты, когда респондент заполняет опрос, но не всегда. Дата опроса записывается автоматически и в согласованном формате и легко переводится в POSIX с использованием as.Date, поэтому элементы, содержащие только время, могут быть проигнорированы и объединены с датой, когда они заполнили опрос.

Ваши мысли очень ценятся.

Примечание1. Некоторые из вас могут сказать, что вы должны были выполнить X, Y или Z в терминах проверки ответов. Для вас, я говорю - черт возьми, в следующий раз. Я не проектировал его! Мне просто нужно иметь дело с этим.

Несколько фактов, которые могут помочь в обходном пути:

  • Время всегда будет рабочим днем, с 9:00 до 18:00 (отсюда am/pm не имеет значения)
  • Годы не имеют значения, так как я могу вытащить их из другого поля (это всегда будет только когда-либо 2011/2010, что, к счастью, вне возможных временных рамок в любых обозначениях)
  • Мне не нужны временные интервалы, так как у меня есть их географическое расположение.

Что я сделал до сих пор:

mos <- strsplit('
jan
feb
mar
apr
may
jun
jul
aug
sep
oct
nov
dec
january
february
march
april
may
june
july
august
september
october
november
december
', '\n')[[1]][-1]

days <- strsplit('
mon
tue
wed
thu
fri
sat
sun
monday
tuesday
wednesday
thursday
friday
saturday
sunday
', '\n')[[1]][-1]
## Messy Date Wrangling
x <- ## that hot ghetto mess above
# minimize
x <- tolower(x)
# remove unnecessary crap
x <- sub("2011"," ",x)
x <- sub("2010"," ",x)
x <- sub("am"," ",x)
x <- sub("pm"," ",x)
x <- sub("[p][.][m]"," ",x)
x <- sub("[a][.][m]"," ",x)
x <- sub("[.]{3}"," ",x)
x <- str_trim(x, side="both")
# divide
x <- strsplit(x,c(" "))
# conquer?

lapply(x, function(x) pmatch(x,mos))
lapply(x, function(x) pmatch(x,days))

Ответ 1

Мое сочувствие, что ваша дата не получилась столь же красивой, как ожидалось.; -)

Я построил (все еще частичное) решение по линиям, предложенным @Rguy.

(Обратите внимание, что этот код по-прежнему имеет ошибку: он всегда возвращает правильное время. По какой-то причине он не всегда делает жадное соответствие цифр перед двоеточием, поэтому иногда возвращается 1:00 когда время 11:00.)

Сначала создайте вспомогательную функцию, которая обтекает gsub и grep. Эта функция принимает вектор символов как один из его аргументов и сворачивает это в одну строку, разделенную |. Эффект этого заключается в том, чтобы вы могли легко передать несколько шаблонов, которые будут соответствовать регулярному выражению:

find.pattern <- function(x, pattern_list){
  pattern <- paste(pattern_list, collapse="|")
  ret <- gsub(paste("^.*(", pattern, ").*", sep=""), "\\1", x, ignore.case=TRUE)
  ret[ret==x] <- NA 
  ret2 <- grepl(paste("^(", pattern, ")$", sep=""), x, ignore.case=TRUE)
  ret[ret2] <- x[ret2] 
  ret
}

Далее, используйте некоторые встроенные имена переменных для создания вектора месяцев и сокращений:

all.month <- c(month.name, month.abb)

Наконец, создайте кадр данных с различными выдержками:

ret <- data.frame(
    data = dat, 
    date1 = find.pattern(dat, "\\d+/\\d+/\\d+"),
    date2 = find.pattern(dat, 
      paste(all.month, "\\s*\\d+[(th)|,]*\\s{0,3}[(2010)|(2011)]*", collapse="|", sep="")),
    year = find.pattern(dat, c(2010, 2011)),
    month = find.pattern(dat, month.abb), #Use base R variable called month.abb for month names
    hour = find.pattern(dat, c("\\d+[\\.:h]\\d+", "12 noon")),
    ampm = find.pattern(dat, c("am", "pm"))
)

Результаты:

head(ret, 50)
                      data  date1        date2 year month  hour ampm
20   April 4th around 10am   <NA>   April 4th  <NA>   Apr  <NA>   am
21   April 4th around 10am   <NA>   April 4th  <NA>   Apr  <NA>   am
22     Mar 18, 2011 9:33am   <NA> Mar 18, 2011 2011   Mar  9:33   am
23     Mar 18, 2011 9:27am   <NA> Mar 18, 2011 2011   Mar  9:27   am
24                      df   <NA>         <NA> <NA>  <NA>  <NA> <NA>
25                      fg   <NA>         <NA> <NA>  <NA>  <NA> <NA>
26                   12:16   <NA>         <NA> <NA>  <NA> 12:16 <NA>
27                    9:50   <NA>         <NA> <NA>  <NA>  9:50 <NA>
28   Feb 8, 2011 / 12:20pm   <NA>  Feb 8, 2011 2011   Feb  2:20   pm
29         8:34 am  2/4/11 2/4/11         <NA> <NA>  <NA>  8:34   am
30     Jan 31, 2011 2:50pm   <NA> Jan 31, 2011 2011   Jan  2:50   pm
31     Jan 31, 2011 2:45pm   <NA> Jan 31, 2011 2011   Jan  2:45   pm
32     Jan 31, 2011 2:38pm   <NA> Jan 31, 2011 2011   Jan  2:38   pm
33     Jan 31, 2011 2:26pm   <NA> Jan 31, 2011 2011   Jan  2:26   pm
34                   11h09   <NA>         <NA> <NA>  <NA> 11h09 <NA>
35                11:00 am   <NA>         <NA> <NA>  <NA>  1:00   am
36                 1h02 pm   <NA>         <NA> <NA>  <NA>  1h02   pm
37                   10h03   <NA>         <NA> <NA>  <NA> 10h03 <NA>
38                    2h10   <NA>         <NA> <NA>  <NA>  2h10 <NA>
39 Jan 13, 2011 9:50am Van   <NA> Jan 13, 2011 2011   Jan  9:50   am
40            Jan 12, 2011   <NA> Jan 12, 2011 2011   Jan  <NA> <NA>

Ответ 2

Это может быть один из немногих случаев, когда лучше использовать другой инструмент, отличный от R. Я знаю, что есть несколько модулей для Perl, которые уже были разработаны для разбора беспорядочно выглядящих дат, в модуле DateTime:: Format:: Natural:: Lang:: EN могут анализировать строки, такие как: "1st tuesday last november". Кажется, я помню еще один модуль, который мог бы понять такие вещи, как "второй вторник после первого понедельника в феврале".

Существует также инструмент http://www.datasciencetoolkit.org/, который захватывает то, что выглядит как даты в тексте, и преобразует их в стандартный формат.

Ответ 3

Я не собираюсь сейчас писать эту функцию, но у меня есть идея, которая может работать.

Поиск каждой строки для 4-значного числа для вызова года.

Используйте grep для поиска каждой строки для первых трех букв аббревиатуры за месяцы. Кажется, что почти все ваши данные (по крайней мере выше) имеют такой идентификатор. Я бы сохранил значение, которое находится в векторе "месяцев", и ставит пробелы везде, где никакое значение не найдено. Вот действительно уродливая версия кода (я сделаю это более эффективным позже и добавлю случай, когда месяц не заглавный!)

mos <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")   
blah <- lapply(1:12, function(i) grepl(mos[i], test))   
lapply(blah, function(i) which(i))   
months <- 0*(1:length(test))   
for (i in 1:12) {   
  months[blah[[i]]] <- i   
}  


   months
  [1]  5  0  0  4  0  4  4  4  4  4  4  4  4  4  4  4  4  0  4  4  4  3  3  0  0  0  0  2  0  1
 [31]  1  1  1  0  0  0  0  0  1  1  1  1  1  1  0  0  0  0  0  0  0  0  0  0  0 12 12 12 12  0
 [61]  0  0  0  0 12 12 12 12  0 12 12 12 12 12 12 12 12 12  0  0  0 12 12 12 12 11 11  0 11 11
 [91] 11  0 11  0 11  0 11  0  0 11 11 11  0 11  0 11 11 11  0 11 11 11 11  0 11  0  0  0 10 10
[121] 10  0 10 10 10  0  0 10 10 10  0  0  0  0  0 10 10  0  0 10 10 10 10  0 10  0 10  0  0  0
[151] 10  0 10 10 10 10 10  9  9  9  9  8  0  0 

"День" чаще всего следует за словом, используемым в течение месяца. Поэтому, если после месяца (то есть символа) есть одно или двухзначное число, извлеките этот номер и назовите его днем.

Времена чаще всего имеют ":" или ".". символ в них, и поэтому найдите каждую строку для этого символа. Если найденный в строке, создайте вектор "Время" со всеми цифрами непосредственно перед и после этого символа (теоретически, включая 2 до и 2 после не должен вызывать проблемы). Поместите пробелы всякий раз, когда символ отсутствует. Было бы неплохо, если бы все данные были определенно ограничены 12-часовым периодом, потому что тогда вам не придется беспокоиться о AM и PM. Если нет, возможно, ищите строку для "AM" и "PM".

Затем попробуйте преобразовать строки, которые имеют все четыре из приведенных выше, в POSIXct. Те, которые не конвертируются, вам придется вручную вводить, конечно. Я думаю, мне понадобилось бы несколько часов, чтобы закодировать функцию, описанную выше, и в зависимости от изменчивости и размера вашего набора данных это может или не стоит того. Кроме того, существует некоторая опасность для неправильных выходов, поэтому добавление приемлемого временного диапазона поможет избежать этого.

В общем, похоже, что вам нужно будет закодировать функцию с целым рядом исключений, а в конце концов, в конце концов, закончится ручная кодировка. Я надеюсь, что кто-то может предложить вам лучшее решение.

Удачи!

Ответ 4

Вольфрам альфа http://www.wolframalpha.com/ это отличный инструмент для выполнения этой работы.

По крайней мере, он успешно интерпретирует некоторые беспорядочные данные в ваших данных. Было бы полезно попробовать.

Я не уверен, подходит ли сайт для чрезвычайно большого набора данных, но если данные не такие большие, это будет полезно.

Нетрудно написать автоматизированный script, который отправляет запрос, получает данные и анализирует его, хотя я не уверен, разрешает ли сайт такое использование.

Ответ 5

Другие уже рассмотрели стандартные подходы и пакеты. Я буду придерживаться другой точки зрения. Использование регулярных выражений и фиксированных форматов даст вам большую часть пути. В остальном я просто подхожу к нему, как к любой проблеме в "сопоставлении шаблонов": статистические методы или машинное обучение. Вы уже указали диапазоны дат и времени, а также метка времени журналов. Извлекая много текстовых функций (это будет полезно для регулярных выражений), вы можете попытаться сопоставить время интереса.

Для получения этой работы нужно всего три вещи:

  • Удаление функций
  • Создание набора обучающих программ
  • Создание и развертывание моделей

Построение и развертывание моделей? Позвольте мне познакомить вас с моим другом R и представлением задачи машинного обучения.:) Основные модели для изучения включают многомерные модели (посмотрите glmnet), деревья решений и поддерживающие векторные машины. Вы можете использовать деревья решений и SVM в качестве входных данных для мультиномиальной модели (и, вероятно, SVM не обязательно будут нужны). Честно говоря, эта часть является туманной: можно было бы сделать это моделирование как отключенные компоненты даты или как процесс уточнений, например. получить год, если это возможно, то минуты (потому что диапазон намного больше, чем в течение часов, дней, месяцев), затем день месяца и, наконец, часы и месяцы. По существу, я хотел бы попытаться идентифицировать "части времени" (аналогичные частям речи) для числовых/строковых компонентов.

Удаление функции: я бы попытался расщепить двоеточия, запятые, косые черты, тире, периоды и т.д. Все, что не является числовым значением. Затем я создавал наборы данных на основе функций в порядке и в любом порядке (т.е. Значение индикатора видимых объектов, игнорируя позиции).

Данные обучения: Amazon Mechanical Turk.

Или, вы знаете, что, просто игнорируйте все это программирование и статистический mumbo jumbo и отправляйте все в Mechanical Turk.:)