Попытка использовать определяемую пользователем функцию для заполнения нового столбца в dataframe. Что происходит не так?

#r #dataframe #subset #replace

#r #dataframe #подмножество #заменить

Вопрос:

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

 TestDF$ELN<-EmployeeLocationNumber(TestDF$Location)
  

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

Пожалуйста, обратите внимание: я пытаюсь понять 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 сотрудников находился в течение года в нескольких местах.

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

Функция 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 amp; Month<=LocationDate,select=Month)[[1]])
  return(LocationNumber)
}
  

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

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

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

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

  3. Я уверен, что функция, которую я использую, смехотворно не похожа на R…что я должен был сделать, чтобы по существу эмулировать запрос типа ВНУТРЕННЕГО соединения?

Комментарии:

1. Я не совсем понимаю, почему EmployeeLocationNumber(8) = 2 ? Emplyee 3 был в местоположениях 4,2 и 8, поэтому я думаю, что число было 3, а не 2?

2. Вы правы, это была опечатка. Это должно было быть EmployeeLocationNumber(3) . Извините за путаницу.

3. Спасибо всем за информацию. Я ранее не видел некоторые из этих функций [с помощью, которые векторизируют], поэтому мне было полезно указать на них. К сожалению, задавая так много вопросов, трудно выбрать подходящего человека, который скажет «ответил» на мой вопрос.

Ответ №1:

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

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

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

Комментарии:

1. Спасибо за иллюстрацию логического индексирования и функции which() .

Ответ №2:

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

Б) В каком смысле Location:8 является «вторым посещенным местоположением»?

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

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

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

  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 amp; Employee==3, select= Location)
#   Location
# 8        2
  

Комментарии:

1. Я думаю, вы хотите FUN=rank , не FUN=order

2. Мои результаты возвращают то, о чем, как я думал, просил @David R, но теперь, когда я снова смотрю на это, я думаю, что мы оба ошибаемся, и функция должна seq фиксировать последовательность посещений.

3. Если вы сделаете предварительный заказ по месяцам, это seq сработает. Но если вы не сделаете предварительный заказ, я уверен, что это так rank

4. Я так не думаю. В значении нет информации о порядке или последовательности Location . С таким же успехом это может быть фактором.

5. О, точно. Это разница между использованием ave(Location,... и ave(Month,... тем, где есть информация о заказе.

Ответ №3:

Векторизованный характер 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)
  

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

Комментарии:

1. Спасибо, что ответили на мой вопрос о построчной вещи. Я не думал об этом таким образом, потому что мое основное использование переменной df $ было в функциях «подмножества», где каждый отдельный элемент вектора можно протестировать, чтобы определить, включена строка или нет.

Ответ №4:

Ваша 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. Dito.

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

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

… Я предполагаю 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
  

Комментарии:

1. Также спасибо за ответы на мои 3 пронумерованных вопроса (см. Мой комментарий Брайану ниже).

2. Есть ли новое понимание числа 2? Каков наилучший способ ссылаться на значение в фрейме данных?

3. @DavidR некоторые идеи см. В Обновленном ответе… Это то, что вы имели в виду?

4. На самом деле, я просто ищу одно значение в этой строке. В общем, я ищу «правильный» способ сказать: «Найдите мне значение [атрибута B] для строки, в которой значение [атрибута A] равно [значению Z]». Другими словами, я пытаюсь найти лучший способ придуматьфрейм данных как разграничение таблицы функций. [Все это предполагает, что строки достаточно различны, чтобы запрос был четко определен, ответ один и тот же, независимо от того, какую строку вы выбираете, где значение [атрибута A] равно [значению Z] .

5. Этот метод очень медленный.