2011-01-04 4 views
4

Я борюсь с чем-то очень простым: сортировка кадра данных на основе формата времени (месяц-год или «% B-% y» в этом случае) , Моя цель - рассчитать различные ежемесячные статистические данные, начиная с суммы.Сортировка фрейма данных на основе месячного формата времени

Часть соответствующей части кадра данных выглядит следующим образом (Это идет хорошо, и в соответствии с моей целью я включаю его здесь, чтобы показать, где проблема может происходит из.):

> tmp09 
    Instrument AccountValue monthYear ExitTime 
1   JPM   6997 april-07 2007-04-10 
2   JPM   7261  mei-07 2007-05-29 
3   JPM   7545  juli-07 2007-07-18 
4   JPM   7614  juli-07 2007-07-19 
5   JPM   7897 augustus-07 2007-08-22 
10  JPM   7423 november-07 2007-11-02 
11  KFT   6992  mei-07 2007-05-14 
12  KFT   6944  mei-07 2007-05-21 
13  KFT   7069  juli-07 2007-07-09 
14  KFT   6919  juli-07 2007-07-16 
# Order on the exit time, which corresponds with 'monthYear' 
> tmp09.sorted <- tmp09[order(tmp09$ExitTime),] 
> tmp09.sorted 
    Instrument AccountValue monthYear ExitTime 
1   JPM   6997 april-07 2007-04-10 
11  KFT   6992  mei-07 2007-05-14 
12  KFT   6944  mei-07 2007-05-21 
2   JPM   7261  mei-07 2007-05-29 
13  KFT   7069  juli-07 2007-07-09 
14  KFT   6919  juli-07 2007-07-16 
3   JPM   7545  juli-07 2007-07-18 
4   JPM   7614  juli-07 2007-07-19 
5   JPM   7897 augustus-07 2007-08-22 
10  JPM   7423 november-07 2007-11-02 

Пока что так хорошо, и сортировка основана на работе ExitTime. Проблема начинается тогда, когда я пытаюсь вычислить суммы в месяц, а затем попытки сортировать этот вывод:

# Calculate the total results per month 
> Tmp09Totals <- tapply(tmp09.sorted$AccountValue, tmp09.sorted$monthYear, sum) 
> Tmp09Totals <- data.frame(Tmp09Totals) 
> Tmp09Totals 
      Tmp09Totals 
april-07   6997 
augustus-07  7897 
juli-07   29147 
mei-07   21197 
november-07  7423 

Как сортировать этот вывод в хронологическом порядке?

Я уже пробовал (помимо различных попыток конвертировать формат monthYear в другой формат даты): упорядочить, сортировать, сортировать, сортировать_df, изменять форму и вычислять сумму на основе tapply, lapply, sapply, aggregate. И даже переписывая имена розеток (давая им число от 1 до длины (tmp09.sorted2 $ AccountValue)), не работает. Я также пытался дать каждому месяцу год другой идентификатор, основанный на том, что я узнал по другому вопросу, но R также испытывал трудности с различиями между различными месячными значениями.

Правильный порядок этого вывода будет апрель-07, мэй-07, Juli-07, augustus07, ноябрь-07:

apr-07 6997 
mei-07 21197 
jul-07 29147 
aug-07 7897 
nov-07 7423 

Я из идей, у вас есть?

ответ

9

Было бы легче иметь отдельные Month и Year факторов, в правильном порядке, и использовать tapply на объединении двух переменных, например:

## The Month factor 
tmp09 <- within(tmp09, 
       Month <- droplevels(factor(strftime(ExitTime, format = "%B"), 
                levels = month.name))) 
## for @Jura25's locale, we can't use the in built English constant 
## instead, we can use this solution, from ?month.name: 
## format(ISOdate(2000, 1:12, 1), "%B")) 
tmp09 <- within(tmp09, 
       Month <- droplevels(factor(strftime(ExitTime, format = "%B"), 
                levels = format(ISOdate(2000, 1:12, 1), "%B")))) 
## 
## And the Year factor 
tmp09 <- within(tmp09, Year <- factor(strftime(ExitTime, format = "%Y"))) 

Который дает нам (в моей местности):

> head(tmp09) 
    Instrument AccountValue monthYear ExitTime Month Year 
1   JPM   6997 april-07 2007-04-10 April 2007 
2   JPM   7261  mei-07 2007-05-29  May 2007 
3   JPM   7545  juli-07 2007-07-18  July 2007 
4   JPM   7614  juli-07 2007-07-19  July 2007 
5   JPM   7897 augustus-07 2007-08-22 August 2007 
10  JPM   7423 november-07 2007-11-02 November 2007 

Затем используйте tapply с обоими факторами:

> with(tmp09, tapply(AccountValue, list(Month, Year), sum)) 
      2007 
April  6997 
May  21197 
July  29147 
August 7897 
November 7423 

или через aggregate:

> with(tmp09, aggregate(AccountValue, list(Month = Month, Year = Year), sum)) 
    Month Year  x 
1 April 2007 6997 
2  May 2007 21197 
3  July 2007 29147 
4 August 2007 7897 
5 November 2007 7423 
+0

Спасибо за ваш подробный ответ Гэвин! Это сработало, как хотелось бы. В особенности совокупная функция дает хороший сложный вывод за разные годы в полном наборе данных (а также достаточно эффективна для различных расчетов). Я получил ошибку с 'levels = month.name', но после замены месяца.имя с пользовательским вектором с именами месяцев в моей локали, эта проблема была решена. :) Благодаря! – Jura25

+0

@ Jura25; да, извините - это английские месяцы ... '? month.name' имеет этот пример для имен месяцев в вашем текущем языковом формате:' format (ISOdate (2000, 1:12, 1), "% B") ', который может спасти вас от ввода имени месяца каждый раз, когда вы хотите их использовать. –

+0

Не нужно извиняться. :) Я в первую очередь упомянул об этом здесь, если кто-то другой сможет его использовать. Спасибо за функцию ISOdate, я не знал об этом, и это действительно очень удобно. – Jura25

1

Редактировать: Я неправильно понял вопрос вначале. Скопируйте данные, приведенные в этом вопросе, а затем

> tmp09 <- read.table(file="clipboard", header=TRUE) 
> Sys.setlocale(category="LC_TIME", locale="Dutch_Belgium.1252") 
[1] "Dutch_Belgium.1252" 

# create POSIXlt variable from monthYear 
> tmp09$d <- strptime(paste("2007", tmp09$monthYear, sep="-"), "%Y-%B-%d") 

# create ordered factor 
> tmp09$dFac <- droplevels(cut(tmp09$d, breaks="month", ordered=TRUE)) 
> tmp09[order(tmp09$d), ] 
    Instrument AccountValue monthYear ExitTime   d  dFac 
1   JPM   6997 april-07 2007-04-10 2007-04-07 2007-04-01 
2   JPM   7261  mei-07 2007-05-29 2007-05-07 2007-05-01 
11  KFT   6992  mei-07 2007-05-14 2007-05-07 2007-05-01 
12  KFT   6944  mei-07 2007-05-21 2007-05-07 2007-05-01 
3   JPM   7545  juli-07 2007-07-18 2007-07-07 2007-07-01 
4   JPM   7614  juli-07 2007-07-19 2007-07-07 2007-07-01 
13  KFT   7069  juli-07 2007-07-09 2007-07-07 2007-07-01 
14  KFT   6919  juli-07 2007-07-16 2007-07-07 2007-07-01 
5   JPM   7897 augustus-07 2007-08-22 2007-08-07 2007-08-01 
10  JPM   7423 november-07 2007-11-02 2007-11-07 2007-11-01 

> Tmp09Totals <- tapply(tmp09$AccountValue, tmp09$dFac, sum) 
> Tmp09Totals 
2007-04-01 2007-05-01 2007-07-01 2007-08-01 2007-11-01 
     6997  21197  29147  7897  7423 
+0

спасибо за ответы. Функция Sys.setlocale() - очень хорошая идея, но я боюсь, что это не имело большого значения. Новый столбец (tmp09 $ d) имеет некоторые ошибки, но сортировка по столбцу ExitTime действительно работает. Но меня особенно интересует сортировка столбца Tmp09Totals, в котором содержится сумма разных месяцев по столбцу monthYear. Мне очень жаль, если мой ответ не был достаточно ясным, я отредактирую его, чтобы лучше прояснить мою мысль. Тем не менее, спасибо за ответ и предложение решения. Очень признателен! – Jura25

+1

Спасибо за разъяснение. Отсутствующим шагом было создание упорядоченного коэффициента с даты с помощью 'cut()'. Надеюсь, это ближе к тому, что вы имели в виду. – caracal

+0

Спасибо Caracal за ваш ответ! Он действительно дает результат, который я искал, но с несколькими годами функция пасты дает некоторые проблемы с каждым годом как «2007». Тем не менее, я выучил некоторые полезные «трюки» (например, вырезать) из вашего ответа, поэтому не все ваши усилия были напрасны. Благодаря! :) – Jura25

1

Похоже, главная проблема заключается в том, чтобы сортировать последовательность строк Месяц-Год в хронологическом порядке. Самый простой способ - предварительно отложить «01» в начале каждой строки месяца-месяца и отсортировать их как обычные даты. Так ваш окончательный данные-кадр Tmp09Totals и сделать это:

monYear <- rownames(Tmp09Totals) 
sortedMonYear <- format(sort(as.Date(paste('01-', monYear, sep = ''), 
             '%d-%B-%y')), 
         '%B-%y') 
Tmp09Totals[ sortedMonYear, , drop = FALSE] 
+0

Но вам не нужно * никаких * из этого, если вы в первую очередь получаете свои факторы в правильном порядке. Вы отвечаете на изменение порядка вывода. Однако, если вы не получите ввод в логическом порядке, вы должны переупорядочить каждый вывод, который вы производите из этих данных. –

+0

@ Gavin согласился, хороший момент ... Ваш подход более общий –

+0

Спасибо, что ответили на Prasad. Это выглядит несколько сложнее, но я только что проверил его, и он работает хорошо (даже с несколькими годами). Мне очень нравится ваш инновационный способ вставить '01' перед 'monthYear', чтобы иметь возможность конвертировать его в обычную дату. Хороший момент, чтобы помнить в моих дальнейших приключениях R. :) Благодаря! – Jura25

3

Вы можете изменить порядок уровней фактора с помощью reorder функции.

tmp09$monthYear <- reorder(tmp09$monthYear, as.numeric(as.Date(tmp09$ExitTime))) 

Хитрость заключается в том, чтобы использовать числовое представление даты, числа дней, прошедших с 1970-01-01 (см ?Date) и использовать среднее значение него в качестве ссылки.

+0

Это хороший способ сделать это! –

+0

Спасибо Мареку, это действительно эффективный способ сделать это. К сожалению, я не работаю для меня. 'reorder (tmp09 $ monthYear, as.numeric (as.Date (tmp09 $ ExitTime))) [1] апрель-07 mei-07 juli-07 juli-07 augustus-07 ноябрь-07 [7] mei-07 mei-07 juli-07 juli-07'. Возможно, переупорядочение не «знает» мой текущий язык? – Jura25

+1

@ Jura25 'reorder' не изменяет значения, а порядок уровней в коэффициенте. Вы увидите эффект, когда вы назовете «tapply» на измененные данные. Что вы получите, если вы запустите 'tapply (tmp09.sorted $ AccountValue, reorder (tmp09.sorted $ monthYear, as.numeric (as.Date (tmp09.sorted $ ExitTime))), sum)'? – Marek

4

Попробуйте использовать класс "yearmon" в зоопарке, так как он сортируется соответствующим образом. Ниже мы создаем образец кадра данных DF, а затем добавляем столбец YearMonth класса "yearmon". Наконец, мы выполняем нашу агрегацию. Фактическая обработка - это только две последние строки (другая часть - просто создать образец кадра данных).

Lines <- "Instrument AccountValue monthYear ExitTime 
JPM   6997 april-07 2007-04-10 
JPM   7261  mei-07 2007-05-29 
JPM   7545  juli-07 2007-07-18 
JPM   7614  juli-07 2007-07-19 
JPM   7897 augustus-07 2007-08-22 
JPM   7423 november-07 2007-11-02 
KFT   6992  mei-07 2007-05-14 
KFT   6944  mei-07 2007-05-21 
KFT   7069  juli-07 2007-07-09 
KFT   6919  juli-07 2007-07-16" 
library(zoo) 
DF <- read.table(textConnection(Lines), header = TRUE) 

DF$YearMonth <- as.yearmon(DF$ExitTime) 
aggregate(AccountValue ~ YearMonth + Instrument, DF, sum) 

Это дает следующее:

> aggregate(AccountValue ~ YearMonth + Instrument, DF, sum) 
    YearMonth Instrument AccountValue 
1 Apr 2007  JPM   6997 
2 May 2007  JPM   7261 
3 Jul 2007  JPM  15159 
4 Aug 2007  JPM   7897 
5 Nov 2007  JPM   7423 
6 May 2007  KFT  13936 
7 Jul 2007  KFT  13988 

Несколько иной подход и выход использует read.zoo напрямую. Он производит одну колонку на инструмент и одну строку в год/месяц. Мы читаем в столбцах, присваивая им соответствующие классы, используя "NULL" для столбца monthYear, так как мы не будем использовать его. Мы также указываем, что индекс времени является третьим столбцом остальных столбцов и что мы хотим, чтобы входные данные разбивались на столбцы на 1-й столбец. FUN=as.yearmon указывает, что мы хотим, чтобы индекс времени был преобразован из класса "Date" в класс "yearmon", и мы суммируем все, используя sum.

z <- read.zoo(textConnection(Lines), header = TRUE, index = 3, 
    split = 1, colClasses = c("character", "numeric", "NULL", "Date"), 
    FUN = as.yearmon, aggregate = sum) 

Результирующий объект зоопарк выглядит следующим образом:

> z 
      JPM KFT 
Apr 2007 6997 NA 
May 2007 7261 13936 
Jul 2007 15159 13988 
Aug 2007 7897 NA 
Nov 2007 7423 NA 

Мы можем предпочесть, чтобы сохранить его в качестве объекта зоопарка, чтобы воспользоваться другими функциями в зоопарке или мы можем преобразовать его в кадр данных, например, это: data.frame(Time = time(z), coredata(z)), который делает время отдельным столбцом или as.data.frame(z), который использует имена строк для времени. fortify.zoo()z) также работает.

+0

Спасибо Г. Гротендик! Мне очень нравится вывод функции as.yearmon, что избавляет меня от использования нескольких столбцов (год и месяц) в качестве списка, и это дает более приятный результат. Я принял это в свой сценарий, чтобы сделать его более всеобъемлющим. :) – Jura25

0

старый пост, но достойный data.table подхода:

Читайте в данных и установить локальный, как описано @caracal

> Sys.setlocale(category="LC_TIME", locale="Dutch_Belgium.1252") 
[1] "Dutch_Belgium.1252" 
> tmp09 <- read.table(file="clipboard", header=TRUE) 
> tmp09$ExitTime <- as.Date(tmp09$ExitTime) 

Обобщить данные по запросу

require(data.table) 
> data.table(tmp09)[, 
+     .(Tmp09Total = sum(AccountValue)), 
+     by = .(Date = format(ExitTime, "%B-%y"))] 
      Date Tmp09Total 
1: april-07  6997 
2:  mei-07  21197 
3:  juli-07  29147 
4: augustus-07  7897 
5: november-07  7423