#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, у меня есть несколько вопросов:
-
Почему не
TestDF$ELN<-EmployeeLocationNumber(TestDF$Location)
будет работать строка за строкой, как это делают другие операторы присваивания? -
Есть ли более простой способ ссылаться на определенное значение в фрейме данных на основе значения другого? Возможно, тот, который не возвращает dataframe / список, из которого затем нужно сгладить и извлечь?
-
Я уверен, что функция, которую я использую, смехотворно не похожа на 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
. Если бы значение было вектором той же длины, что и количество строк, оно работало бы так, как вы хотели. - Я вернусь к вам по этому поводу 🙂
- 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. Этот метод очень медленный.