#r #na
#r #na
Вопрос:
У меня есть фрейм данных, который является результатом левого соединения. Примерные данные приведены ниже:
P.I.D.. Status List.year CURRENT_LAND_VALUE CURRENT_IMPROVEMENT_VALUE
<chr> <chr> <dbl> <dbl> <dbl>
1 003-913-627 X 2000 NA NA
2 003-913-627 T 2010 1578000 1201000
3 003-913-627 S 2018 NA NA
4 003-913-627 S 2018 2814000 901000
5 003-913-627 S 2002 NA NA
6 003-913-627 T 2007 390000 282000
7 003-913-627 T 2007 295000 180000
8 003-913-627 S 2008 464000 391000
9 003-913-627 S 2008 339000 246000
10 003-913-627 X 2009 339000 246000
11 003-913-627 X 2009 464000 391000
Извините, я пытался использовать dput
для генерации кода для данных, но когда я попытался, это дало мне какой-то несвязанный результат, который не представляет таблицу, показанную выше
Как видно для 2018 года и PID 003-913-627, показаны две строки. У одного есть номер для CURRENT_LAND_VALUE и CURRENT_IMPROVEMENT_VALUE, а одна строка включает NA . Что я хочу сделать, так это удалить строку, которая имеет значение NA, только если строка дублируется (что означает, что у нас есть другая строка с тем же PID и List.year. В некоторых случаях, таких как первая строка, поскольку нет такой же строки с PID 003-913-627 и List.Year 2000, NA не следует удалять. Ожидаемый результат для вышеуказанного фрейма данных:
P.I.D.. Status List.year CURRENT_LAND_VALUE CURRENT_IMPROVEMENT_VALUE
<chr> <chr> <dbl> <dbl> <dbl>
1 003-913-627 X 2000 NA NA
2 003-913-627 T 2010 1578000 1201000
4 003-913-627 S 2018 2814000 901000
5 003-913-627 S 2002 NA NA
6 003-913-627 T 2007 390000 282000
7 003-913-627 T 2007 295000 180000
8 003-913-627 S 2008 464000 391000
9 003-913-627 S 2008 339000 246000
10 003-913-627 X 2009 339000 246000
11 003-913-627 X 2009 464000 391000
В заключение: я хочу удалить строки, содержащие NA в «CURRENT_LAND_VALUE» и «CURRENT_IMPROVEMENT_VALUE», только если уже есть строка с такими же «PID» и «List.Year», которая имеет фактическое значение для «CURRENT_IMPROVEMENT_VALUE» или «CURRENT_LAND_VALUE»
как я могу это сделать?
Ответ №1:
summarise
Здесь можно использовать группировку after, поскольку dplyr
version >= 1.0
может возвращать более одной строки на группу. Здесь мы можем использовать столбцы группировки, а затем выполнить summarise
across
числовые столбцы, чтобы вернуть элементы, отличные от NA, если хотя бы один из них не является NA, или вернуть NA
library(dplyr)
df1 %>%
group_by(`P.I.D..`, Status, List.year) %>%
summarise(across(where(is.numeric),
~ if(all(is.na(.))) NA_real_ else .[complete.cases(.)]), .groups = 'drop')
-вывод
# A tibble: 10 x 5
# P.I.D.. Status List.year CURRENT_LAND_VALUE CURRENT_IMPROVEMENT_VALUE
# <chr> <chr> <int> <dbl> <dbl>
# 1 003-913-627 S 2002 NA NA
# 2 003-913-627 S 2008 464000 391000
# 3 003-913-627 S 2008 339000 246000
# 4 003-913-627 S 2018 2814000 901000
# 5 003-913-627 T 2007 390000 282000
# 6 003-913-627 T 2007 295000 180000
# 7 003-913-627 T 2010 1578000 1201000
# 8 003-913-627 X 2000 NA NA
# 9 003-913-627 X 2009 339000 246000
#10 003-913-627 X 2009 464000 391000
Подробности — across
циклы по нескольким столбцам. Внутри него первым выражением могут быть интересующие столбцы. Мы могли бы использовать everything()
, если все остальные столбцы должны быть циклическими или выражение для проверки type
столбца, и если оно соответствует только циклу ( where(is.numeric)
) , тогда мы создаем выражение lamdba ( ~
эквивалент функции (x)) и используем некоторое условие if/else
. Возможно, это не требуется, но это просто вариант предотвращения сбоя, когда в некоторых столбцах есть только NA
. В else
, мы подмножествуем столбец элементами, отличными от NA ( .[complete.cases(.)]
) .
Предполагается, что он вернет одинаковую длину для каждого столбца, или же может быть заключен в list
данные
df1 <- structure(list(P.I.D.. = c("003-913-627", "003-913-627", "003-913-627",
"003-913-627", "003-913-627", "003-913-627", "003-913-627", "003-913-627",
"003-913-627", "003-913-627", "003-913-627"), Status = c("X",
"T", "S", "S", "S", "T", "T", "S", "S", "X", "X"), List.year = c(2000L,
2010L, 2018L, 2018L, 2002L, 2007L, 2007L, 2008L, 2008L, 2009L,
2009L), CURRENT_LAND_VALUE = c(NA, 1578000L, NA, 2814000L, NA,
390000L, 295000L, 464000L, 339000L, 339000L, 464000L),
CURRENT_IMPROVEMENT_VALUE = c(NA,
1201000L, NA, 901000L, NA, 282000L, 180000L, 391000L, 246000L,
246000L, 391000L)), class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11"))
Комментарии:
1. Я пытаюсь понять это замечательное выражение для
summarise
; не могли бы вы объяснить его поведение? Я никогда не видел ничего подобного.2. Спасибо, акрун. Я скопировал ваш код, но получил ошибку, касающуюся круглых скобок. Поскольку ваш код намного сложнее, чем я знаю, я не был уверен, где мне следует добавить круглые скобки. Не могли бы вы, пожалуйста, помочь с этим? Кроме того, я был бы признателен, если бы вы могли объяснить подробнее, чтобы я мог понять, как работает этот код
3. @Roozbeh_you Я проверил еще раз, скопировав / вставив и запустив код. Это работает на меня. Можете ли вы показать свой
packageVersion('dplyr')
. Я бы использовал>= 1.0
дляdplyr
4. @акрун. [1] ‘1.0.2’ так что, я думаю, с этой стороны все в порядке. Не уверен, что когда я копирую и вставляю его, он отображается как
unmatched opening brackets
5. @Roozbeh_you Я пробовал пару раз, но это работает
Ответ №2:
Я подозреваю, что это связано с тем, как вы соединяете два фрейма данных, но, не имея возможности их создать, моя первая мысль заключается в том, что вы могли бы сделать что-то вроде:
df %>%
distinct(`P.I.D.`, CURRENT_LAND_VALUE, .keep_all = TRUE) %>%
filter(!is.na(CURRENT_LAND_VALUE))
Это distinct()
даст вам различные входные данные с комбинациями P.I.D
столбца и CURRENT_LAND_VALUE
столбца (таким образом, если он дублируется, вы получите NA
и фактическое значение в виде отдельных строк), а затем оттуда вы можете безопасно удалить любые NA
значения (предполагая NA
, что s только из дублирования, и это не где-то еще в данные).
Если NA
s встречаются естественным образом, это будет сложнее, и я бы сосредоточился на преобразовании full_join
в другой тип соединения.
РЕДАКТИРОВАТЬ: я только что заметил, что естественные NA
s существуют. Тогда мое решение менее полезно.
Ответ №3:
Базовое решение R:
subset(within(df, {
pk <- as.integer(as.factor(paste0(P.I.D.., List.year)))
pk_cnt <- ave(pk, pk, FUN = length)
}), pk_cnt == 1 | !(is.na(CURRENT_LAND_VALUE) amp; is.na(CURRENT_IMPROVEMENT_VALUE)),
select = names(df))
Данные:
df <- structure(list(P.I.D.. = c("003-913-627", "003-913-627", "003-913-627",
"003-913-627", "003-913-627", "003-913-627", "003-913-627", "003-913-627",
"003-913-627", "003-913-627", "003-913-627"), Status = c("X",
"T", "S", "S", "S", "T", "T", "S", "S", "X", "X"), List.year = c(2000L,
2010L, 2018L, 2018L, 2002L, 2007L, 2007L, 2008L, 2008L, 2009L,
2009L), CURRENT_LAND_VALUE = c(NA, 1578000L, NA, 2814000L, NA,
390000L, 295000L, 464000L, 339000L, 339000L, 464000L), CURRENT_IMPROVEMENT_VALUE = c(NA,
1201000L, NA, 901000L, NA, 282000L, 180000L, 391000L, 246000L,
246000L, 391000L)), row.names = c(NA, -11L), class = "data.frame")
Ответ №4:
Вы можете сохранить те строки, которые имеют только одну строку в группе или не имеют NA
значения в обоих CURRENT_LAND_VALUE
и CURRENT_IMPROVEMENT_VALUE
.
library(dplyr)
df %>%
group_by(P.I.D.., List.year) %>%
filter(n() == 1 | !(is.na(CURRENT_LAND_VALUE) amp; is.na(CURRENT_IMPROVEMENT_VALUE)))
# P.I.D.. Status List.year CURRENT_LAND_VALUE CURRENT_IMPROVEMENT_VALUE
# <chr> <chr> <int> <int> <int>
# 1 003-913-627 X 2000 NA NA
# 2 003-913-627 T 2010 1578000 1201000
# 3 003-913-627 S 2018 2814000 901000
# 4 003-913-627 S 2002 NA NA
# 5 003-913-627 T 2007 390000 282000
# 6 003-913-627 T 2007 295000 180000
# 7 003-913-627 S 2008 464000 391000
# 8 003-913-627 S 2008 339000 246000
# 9 003-913-627 X 2009 339000 246000
#10 003-913-627 X 2009 464000 391000