Попытка использовать пользовательскую функцию для заполнения нового столбца в кадре данных.Что не так? - PullRequest
3 голосов
/ 18 октября 2011

Супер короткая версия: я пытаюсь использовать пользовательскую функцию для заполнения нового столбца в кадре данных командой:

TestDF$ELN<-EmployeeLocationNumber(TestDF$Location)

Однако, когда я запускаю команду, кажется, что она просто применяет EmployeeLocationNumber к значению Location первой строки, а не использует значение каждой строки для определения значения нового столбца для этой строки индивидуально.

Обратите внимание: я пытаюсь понять R, а не просто выполнить эту конкретную задачу. Я действительно смог получить вывод, который искал, используя функцию Apply (), но это не имеет значения. Насколько я понимаю, приведенная выше строка должна работать построчно, но это не так.

Вот особенности для тестирования:

TestDF<-data.frame(Employee=c(1,1,1,1,2,2,3,3,3), 
                   Month=c(1,5,6,11,4,10,1,5,10), 
                   Location=c(1,5,6,7,10,3,4,2,8))

Этот testDF отслеживает, где каждый из 3 сотрудников находился в течение года в нескольких местах.

(Вы можете думать о «Месторасположении» как о уникальном для каждого сотрудника ... в сущности, это уникальный идентификатор для этой строки.)

Функция EmployeeLocationNumber берет местоположение и выводит число, указывающее порядок посещения сотрудником этого места. Например, EmployeeLocationNumber(8) = 2, потому что это было второе местоположение, которое посетил сотрудник, посетивший его.

EmployeeLocationNumber <- function(Site){
  CurrentEmployee <- subset(TestDF,Location==Site,select=Employee, drop = TRUE)[[1]]
  LocationDate<- subset(TestDF,Location==Site,select=Month, drop = TRUE)[[1]]
  LocationNumber <- length(subset(TestDF,Employee==CurrentEmployee & Month<=LocationDate,select=Month)[[1]])
  return(LocationNumber)
}

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

Итак, учитывая, что я действительно пытаюсь понять, как работать в R, у меня есть несколько вопросов:

  1. Почему TestDF$ELN<-EmployeeLocationNumber(TestDF$Location) не работает построчно, как другие операторы присваивания?

  2. Есть ли более простой способ ссылаться на определенное значение в кадре данных на основе значения другого? Возможно, тот, который не возвращает фрейм данных / список, который затем должен быть сведен и извлечен из?

  3. Я уверен, что функция, которую я использую, смехотворно не похожа на R ... что я должен был сделать, чтобы по существу эмулировать запрос типа INNER Join?

Ответы [ 4 ]

2 голосов
/ 18 октября 2011

Используя логическое индексирование, сжатая однострочная замена для вашей функции:

EmployeeLocationNumber <- function(Site){
    with(TestDF[do.call(order, TestDF), ], which(Location[Employee==Employee[which(Location==Site)]] == Site))
}

Конечно, это не самый читаемый способ, но он демонстрирует принципы логического индексирования и which() в R. Затем, как уже говорили другие, просто оберните его векторной функцией * ply, чтобы применить это к ваш набор данных.

2 голосов
/ 18 октября 2011

A) TestDF$Location - вектор.Ваша функция не настроена на возврат вектора, поэтому присвоение ему вектора, вероятно, не удастся.

B) В каком смысле Location: 8 "второе посещенное местоположение"?

C)Если вы хотите в групповом порядке, то вам нужно передать ваш разделенный по сотрудникам фрейм данных в функцию, которая вычисляет результат.

D) Условный доступ к data.frame обычно включает в себя логическое индексирование и / или использование которого()

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

 with(TestDF, ave(Location, Employee, FUN=seq))
[1] 1 2 3 4 2 1 2 1 3
 TestDF$LocOrder <-  with(TestDF, ave(Month, Employee, FUN=seq))

Если вы хотеливторое место для EE: 3 это будет:

subset(TestDF, LocOrder==2 & Employee==3, select= Location)
#   Location
# 8        2
1 голос
/ 18 октября 2011

Ваша EmployeeLocationNumber функция принимает вектор и возвращает одно значение.Поэтому назначение для создания нового столбца data.frame просто получает единственное значение:

EmployeeLocationNumber(TestDF$Location) # returns 1

TestDF$ELN<-1 # Creates a new column with the single value 1 everywhere
  1. Назначение не делает подобной магии.Это берет значение и помещает это куда-нибудь.В этом случае значение 1.Если бы значение было вектором такой же длины, что и количество строк, оно работало бы так, как вы хотели.
  2. Я вам еще раз отвечу:)
  3. Дито.

Обновление : Я наконец-то разработал некоторый код для этого, но к тому времени у @DWin есть гораздо лучшее решение: (

TestDF$ELN <- unlist(lapply(split(TestDF, TestDF$Employee), function(x) rank(x$Month)))

... Iугадайте, что функция ave делает в значительной степени то, что делает код выше. Но для записи:

Сначала я split data.frame в подкадры, один на сотрудника. Затем я rankмесяцев (на всякий случай, если ваши месяцы не в порядке). Вы также можете использовать order, но rank может лучше обрабатывать связи. Наконец, я объединяю все результаты в вектор и помещаю его в новый столбец ELN.

Обновите снова Относительно вопроса 2 «Каков наилучший способ ссылки на значение в кадре данных?»:

Это зависит немного от конкретной проблемы, ноесли у вас есть значение, скажем Employee=3 и вы хотите найти все строки в data.frame, которые соответствуют этому, то просто:

TestDF$Employee == 3 # Returns logical vector with TRUE for all rows with Employee == 3
which(TestDF$Employee == 3) # Returns a vector of indices instead
TestDF[which(TestDF$Employee == 3), ] # Subsets the data.frame on Employee == 3
0 голосов
/ 18 октября 2011

Векторизованная природа R (то есть строка за строкой) работает не путем многократного вызова функции с каждым следующим значением аргументов, а путем одновременной передачи всего вектора и одновременной работы со всем ним.Но в EmployeeLocationNumber вы возвращаете только одно значение, поэтому это значение повторяется для всего набора данных.

Кроме того, ваш пример для EmployeeLocationNumber не соответствует вашему описанию.

> EmployeeLocationNumber(8)
[1] 3

Теперь, один из способов векторизации функции так, как вы думаете (повторные вызовы для каждого значения), состоит в том, чтобы передать ее через Vectorize()

TestDF$ELN<-Vectorize(EmployeeLocationNumber)(TestDF$Location)

, что дает

> TestDF
  Employee Month Location ELN
1        1     1        1   1
2        1     5        5   2
3        1     6        6   3
4        1    11        7   4
5        2     4       10   1
6        2    10        3   2
7        3     1        4   1
8        3     5        2   2
9        3    10        8   3

Что касается других ваших вопросов, я бы просто написал это как

TestDF$ELN<-ave(TestDF$Month, TestDF$Employee, FUN=rank)

Логика состоит в том, чтобы взять месяцы, рассматривая группы месяцев по сотрудникам отдельно, и дать мне порядок ранжирования месяцев (куда они попадают по порядку).

...