Работа с грязными датами - PullRequest
36 голосов
/ 01 июня 2011

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

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

Вот краткий пример из тысяч:

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

Ответы [ 5 ]

21 голосов
/ 01 июня 2011

Сочувствую, что твое свидание оказалось не таким красивым, как ожидалось. ; -)

Я построил (все еще частичное) решение по линии, предложенной @ 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>
10 голосов
/ 01 июня 2011

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

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

6 голосов
/ 01 июня 2011

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

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

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

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». </p>

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

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

Удачи!

3 голосов
/ 02 ноября 2011

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

Есть только три способа заставить это работать:

  1. Функция извлечения
  2. Учебный комплект поколения
  3. Сборка и развертывание моделей

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

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

Данные обучения: механический турок Амазонки.

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

3 голосов
/ 01 июня 2011

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...