Как указывает @hendrikvanb, ваши дублирующиеся выходные строки связаны не только со строками, но также с неполными данными и небольшими различиями в некоторых ваших входных строках. Даже если две строки содержат одинаковую информацию для читателя-человека, R рассматривает их как разные, если каждый отдельный символ не является одинаковым. Как только мы решим эту проблему, решение станет намного проще.
Шаг 1: убедитесь, что записи с похожими именами имеют одинаковые имена
Следующий код начинается с некоторой простой очистки (удаление лишних пробелов, создание всего нижний регистр). Затем он ищет в вашей таблице текст, который похож на другой, и для каждой пары запрашивается, хотите ли вы заменить один другим другим.
Например, если в вашем наборе данных содержатся «levi et al. 1990» и «levi et al 1990» один с полной остановкой, а другой без, вы получите сообщение:
Хотите заменить «levi et al. 1990» на «levi et al 1990»?
Вам также будет задан тот же вопрос в обратном порядке. Если вы нажмете «да», то все экземпляры первого будут заменены вторым в вашей базе данных.
library(dplyr)
library(tidyr)
# standardise
standardized <- all_data_final %>%
rename(walltype = `Wall type (Kaminski 2014)`) %>% # first column in example data has odd name
mutate_all(as.character) %>% # ensures all columns are string not factor
mutate_all(trimws) %>% # leading and trailing white space
mutate_all(function(x){gsub(" +"," ",x)}) %>% # remove internal duplicate spaces
mutate_all(tolower) %>% # cast everything to lower
mutate(row = row_number())
# prompt user to merge text that is very close together
tollerance = 2
cols <- c("walltype", "Order", "Superfamily", "Family", "Genus", "Species", "authority", "location")
for(col in cols){
unique_vals = standardized[[col]] %>% unique() %>% sort()
for(val in unique_vals){
for(val2 in unique_vals){
# check if text strings are within edit distance of each other
if(adist(val, val2) > 0 & adist(val, val2) <= tollerance){
msg = paste0("Do you want [", val, "] replaced with [", val2, "] ?")
ans = FALSE
ans = askYesNo(msg) # ask user for every pair of close values
if(ans)
standardized <- mutate_all(standardized, function(x){ifelse(x == val, val2, x)})
}
}
}
}
Вы можете контролировать чувствительность этой проверки, регулируя параметр tollerance
. Вы можете думать об этом как о количестве символов между правильным текстом и орфографической ошибкой.
Шаг 2: сохранить текстовую информацию категории там, где она доступна
Цель здесь - убедиться, что если одна запись из вида имеет порядок, семейство, род или авторитет, то это появляется в финальной таблице. Мы можем сделать это, запросив максимальный порядок / семейство / род для вида.
При работе с текстом max возвращает последнюю запись в алфавитном порядке. Сначала пробел или пробел сортируются по верху, поэтому мы должны использовать max
, так как min
вернет пустые текстовые поля.
Код для этого объединен в шаг 3.
Шаг 3: сохраните метку регистра там, где она доступна
Преобразовав столбец регистра в цифру c, мы можем суммировать все случаи в поисках максимального значения 1. В некоторых случаях NA
или NULL рассматривается как -Inf
, поэтому мы также обрабатываем это.
Следующий код разрешает шаги 2 и 3 в одном и том же операторе summarise_all
.
# collapse
final_result <- standardized %>%
mutate(cases = ifelse(!is.na(cases), 1, 0)) %>%
pivot_wider(names_from = location, values_from = cases) %>%
group_by(Species) %>%
summarise_all(max, na.rm = TRUE) %>% # hack, ideally we'd handle strings and numbers differently
mutate_all(function(x){ifelse(is.infinite(x), NA, x)}) # gets rid of -Inf caused by summarise_all
Вот вывод dput
, который я получаю из этого код:
structure(list(Species = c("", "?planorbulina sp . 1", "acervulina cf. a. mahabethi",
"acervulina inhaerens", "acervulina mabahethi", "acervulina sp. 01",
"adelosina bicornis", "adelosina carinatastriata", "adelosina cf. a. mediterranensis",
"adelosina crassicarinata", "adelosina dagornae", "adelosina echinata",
"adelosina honghensis"), walltype = c("", "hyaline", "hyaline",
"hyaline", "hyaline", "hyaline", "", "porcelaneous (imperforate)",
"porcelaneous (imperforate)", "porcelaneous (imperforate)", "porcelaneous (imperforate)",
"porcelaneous (imperforate)", "porcelaneous (imperforate)"),
Order = c("", "rotaliida", "rotaliida", "rotaliida", "rotaliida",
"rotaliida", "", "miliolida", "miliolida", "miliolida", "miliolida",
"miliolida", "miliolida"), Superfamily = c("", "planorbulinoidea",
"acervulinoidea", "acervulinoidea", "acervulinoidea", "acervulinoidea",
"milioloidea", "milioloidea", "milioloidea", "milioloidea",
"milioloidea", "milioloidea", "milioloidea"), Family = c("",
"planorbulinidae", "acervulinidae", "acervulinidae", "acervulinidae",
"acervulinidae", "cribrolinoididae", "hauerinidae", "cribrolinoididae",
"cribrolinoididae", "cribrolinoididae", "cribrolinoididae",
"cribrolinoididae"), Genus = c("", "?planorbulina", "acervulina",
"acervulina", "acervulina", "acervulina", "adelosina", "quinqueloculina",
"adelosina", "adelosina", "adelosina", "adelosina", "adelosina"
), authority = c("haynesina sp.", "d'orbigny, 1826", "said, 1949",
"schultze, 1854", "said, 1949", "schultze, 1854", "walker & jacob, 1798",
"wiesner 1923", "le calvez & le calvez, 1958", "", "levi et al. 1990",
"d'orbigny, 1826", "lak, 1982"), row = c(2L, 3L, 5L, 7L,
9L, 11L, 13L, 27L, 28L, 32L, 40L, 44L, 50L), `parkar and gischler 2015` = c(1,
NA, NA, NA, 1, NA, NA, 1, NA, NA, NA, NA, NA), `present study` = c(1,
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), `cherif et al. 1997` = c(NA,
1, NA, NA, NA, NA, NA, 1, NA, 1, NA, 1, 1), `amao et al. 2016 mp` = c(NA,
NA, 1, NA, NA, 1, NA, 1, 1, NA, NA, NA, NA), amao_et_al_2019_persian_gulf_paper = c(NA,
NA, 1, NA, NA, 1, NA, NA, NA, NA, NA, NA, NA), `murray 1965` = c(NA,
NA, NA, 1, NA, NA, NA, NA, NA, NA, NA, NA, NA), `shublak 1977` = c(NA,
NA, NA, 1, NA, NA, 1, NA, NA, NA, NA, NA, NA), `khader 2020` = c(NA,
NA, NA, NA, NA, NA, 1, 1, NA, NA, 1, 1, 1), `al-zamel et al 1996` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, 1, NA, NA, NA), `al-zamel et al 2009` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, 1, NA, NA), `amao et al. 2016 salwa` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA), amao_et_al_2019_baseline_paper = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA), `khader 1997` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, 1, NA, NA), `al-ghadban 2000` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, 1, 1, 1), `al-theyabi 2012b` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA), `al-enezi et al. 2019` = c(NA,
NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA), `al-zamel & cherif 1998` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, NA, NA), `al-enezi & frontalini 2015` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, 1, 1, NA, NA), `al-ammar 2011` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, NA), `al-enezi and frontalini 2015` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, NA), `al-shuaibi 1997` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA), `clark and keiji 1975` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1), `nabavi 2014` = c(NA,
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1)), class = c("tbl_df",
"tbl", "data.frame"), row.names = c(NA, -13L))