通过几乎相同的字符串进行总结和传播

summarize and spread by almost identical strings

我从几个具有相似项目的原始 df 开始,清理并合并为长格式,后来我使用 dplyr 将其合并为宽格式...但是,我留下了重复项,因为我正在处理 几乎相同的字符串,任何人都可以建议一种更简单的方法来在传播我的数据时删除重复项。

这是我的代码示例

library(tidyverse)
library(readxl)
library(reprex)

all_data_final_wider<-all_data_final %>%
  mutate(cases = case_when(cases=='X' ~ 'x', cases=='x' ~ 'x'))%>%
  group_by(Species) %>%
  mutate(row = row_number()) %>%
  tidyr::pivot_wider(names_from = location, values_from =cases)%>%
  select(-row)

以下是我的示例数据

dput
structure(list(`Wall type (Kaminski 2014)` = c("", "", "hyaline", 
"hyaline", "hyaline", "hyaline", "", "hyaline", "", "hyaline", 
"hyaline", "", "", "porcelaneous (imperforate)", "porcelaneous (imperforate)", 
"porcelaneous (imperforate)", "porcelaneous (imperforate)", "porcelaneous (imperforate)", 
"", "", "", "", "", "", "", "", "", "porcelaneous (imperforate)", 
"porcelaneous (imperforate)", "porcelaneous (imperforate)", "porcelaneous (imperforate)", 
"porcelaneous (imperforate)", "porcelaneous (imperforate)", "porcelaneous (imperforate)", 
"", "", "", "", "", "", "porcelaneous (imperforate)", "", "", 
"", "porcelaneous (imperforate)", "", "", "", "", ""), Order = c("", 
"", "Rotaliida", "Rotaliida", "Rotaliida", "Rotaliida", "", "Rotaliida", 
"", "Rotaliida", "Rotaliida", "", "", "Miliolida", "Miliolida", 
"Miliolida", "Miliolida", "Miliolida", "Miliolida", "", "", "", 
"", "", "", "", "", "Miliolida", "Miliolida", "Miliolida", "Miliolida", 
"Miliolida", "Miliolida", "Miliolida", "", "", "", "", "", "", 
"Miliolida", "", "", "", "Miliolida", "", "", "", "", ""), Superfamily = c("", 
"", "Planorbulinoidea", "Acervulinoidea", "Acervulinoidea", "Acervulinoidea", 
"", "Acervulinoidea", "Acervulinoidea ", "Acervulinoidea", "Acervulinoidea", 
"Milioloidea", "Milioloidea", "Milioloidea", "Milioloidea", "Milioloidea", 
"Milioloidea", "Milioloidea", "", "", "", "", "", "", "", "", 
"", "Milioloidea", "Milioloidea", "Milioloidea", "Milioloidea", 
"Milioloidea", "Milioloidea", "Milioloidea", "", "", "", "", 
"", "", "Milioloidea", "", "", "", "Milioloidea", "", "", "", 
"", ""), Family = c("", "", "Planorbulinidae", "Acervulinoidae", 
"Acervulinoidae", "Acervulinoidae", "", "Acervulinoidae", "Acervulinidae", 
"Acervulinoidae", "Acervulinoidae", "Cribrolinoididae", "Cribrolinoididae", 
"Cribrolinoididae", "Cribrolinoididae", "Hauerinidae", "Hauerinidae", 
"Hauerinidae", "Hauerinidae", "", "", "", "", "", "", "", "", 
"Cribrolinoididae", "Cribrolinoididae", "Cribrolinoididae", "Cribrolinoididae", 
"Cribrolinoididae", "Cribrolinoididae", "Cribrolinoididae", "", 
"", "", "", "", "", "Cribrolinoididae", "", "", "", "Cribrolinoididae", 
"", "", "", "", ""), Genus = c("", "", "?Planorbulina", "Acervulina", 
"Acervulina", "Acervulina", "", "Acervulina", "Acervulina", "Acervulina", 
"Acervulina", "Adelosina", "Adelosina", "Adelosina", "Adelosina", 
"Adelosina", "Adelosina", "Adelosina", "Quinqueloculina", "", 
"", "", "", "", "", "", "", "Adelosina", "Adelosina", "Adelosina", 
"Adelosina", "Adelosina", "Adelosina", "Adelosina", "", "", "", 
"", "", "", "Adelosina", "", "", "", "Adelosina", "Adelosina", 
"Adelosina", "", "", ""), Species = c("", "", "?Planorbulina sp . 1", 
"Acervulina cf. A. mahabethi", "Acervulina cf. A. mahabeti", 
"Acervulina inhaerens", "Acervulina inhaerens ", "Acervulina mabahethi", 
"Acervulina mabahethi ", "Acervulina sp. 01", "Acervulina sp. 01", 
"Adelosina bicornis ", "Adelosina bicornis ", "Adelosina carinatastriata", 
"Adelosina carinatastriata", "Adelosina carinatastriata", "Adelosina carinatastriata", 
"Adelosina carinatastriata", "Adelosina carinatastriata", "Adelosina carinatastriata ", 
"Adelosina carinatastriata ", "Adelosina carinatastriata ", "Adelosina carinatastriata ", 
"Adelosina carinatastriata ", "Adelosina carinatastriata ", "Adelosina carinatastriata ", 
"Adelosina carinatastriata ", "Adelosina cf. A. mediterranensis", 
"Adelosina crassicarinata", "Adelosina crassicarinata", "Adelosina crassicarinata", 
"Adelosina crassicarinata", "Adelosina dagornae", "Adelosina dagornae", 
"Adelosina dagornae", "Adelosina dagornae", "Adelosina dagornae", 
"Adelosina dagornae", "Adelosina dagornae", "Adelosina dagornae", 
"Adelosina echinata", "Adelosina echinata ", "Adelosina echinata ", 
"Adelosina echinata ", "Adelosina honghensis", "Adelosina honghensis", 
"Adelosina honghensis", "Adelosina honghensis ", "Adelosina honghensis ", 
"Adelosina honghensis "), authority = c("Haynesina sp.", "Haynesina sp.", 
"d'Orbigny, 1826", " Said, 1949 ", "", "Schulze, 1854", "Schulze, 1854", 
" Said, 1949 ", "Said, 1949 ", "Schultze, 1854", "", "Walker & Jacob, 1798 ", 
"Walker & Jacob, 1798 ", " Wiesner, 1923 ", " Wiesner, 1923 ", 
" Wiesner, 1923 ", " Wiesner, 1923 ", " Wiesner, 1923 ", "Wiesner, 1923", 
"Wiesner 1923 ", "Wiesner 1923 ", "Wiesner 1923 ", "Wiesner 1923 ", 
"Wiesner 1923 ", "Wiesner 1923 ", "Wiesner 1923 ", "Wiesner 1923 ", 
" Le Calvez & Le Calvez, 1958 ", "", "", "", "", "", "", "Levi et al. 1990 ", 
"Levi et al. 1990 ", "Levi et al. 1990 ", "Levi et al. 1990 ", 
"Levi et al. 1990 ", "Levi et al. 1990 ", "", "d'Orbigny, 1826", 
"d'Orbigny, 1826", "d'Orbigny, 1826", "", "", "", "Lak, 1982", 
"Lak, 1982", "Lak, 1982"), location = c(" Parkar and Gischler  2015 ", 
"Present study", "Cherif et al. 1997", "Amao et al. 2016 PG", 
"Amao_et_al_2019_Persian_Gulf_paper", "Murray 1965", " Shublak  1977 ", 
"Parker and Gischler 2015", " Parkar and Gischler  2015 ", "Amao et al. 2016 PG", 
"Amao_et_al_2019_Persian_Gulf_paper", " Shublak  1977 ", "Khader  2020 ", 
"Al-Zamel et al 1996", "Al-Zamel et al 2009", "Parker and Gischler 2015", 
"Amao et al. 2016 MP", "Amao et al. 2016 Salwa", "Amao_et_al_2019_baseline_paper", 
"Al-Zamel et al.  1996 ", "Khader  1997 ", " Cherif et al.  1997 ", 
"Al-Ghadban  2000 ", "Al-Zamel et al.  2009 ", "Al-Theyabi  2012b ", 
"Al-Enezi et al.  2019 ", "Khader  2020 ", "Amao et al. 2016 MP", 
"Al-Zamel et al 1996", "Cherif et al. 1997", "Al-Zamel & Cherif 1998", 
"Al-Enezi & Frontalini 2015", "Al-Zamel et al 2009", "Al-Enezi & Frontalini 2015", 
"Khader  1997 ", "Al-Ghadban  2000 ", "Al-Zamel et al.  2009 ", 
"Al-Ammar  2011 ", "Al-Enezi and Frontalini  2015 ", "Khader  2020 ", 
"Cherif et al. 1997", "Al-Shuaibi  1997 ", "Al-Ghadban  2000 ", 
"Khader  2020 ", "Cherif et al. 1997", "Clark and Keiji 1975", 
"Nabavi 2014", " Cherif et al.  1997 ", "Al-Ghadban  2000 ", 
"Khader  2020 "), cases = c("X", "X", "x", "x", "x", "x", "X", 
"x", "X", "x", "x", "X", "X", "x", "x", "x", "x", "x", "x", "X", 
"X", "X", "X", "X", "X", "X", "X", "x", "x", "x", "x", "x", "x", 
"x", "X", "X", "X", "X", "X", "X", "x", "X", "X", "X", "x", "x", 
"x", "X", "X", "X")), row.names = c(NA, -50L), class = c("tbl_df", 
"tbl", "data.frame"))

目前,我的结果看起来像 Before but my target is After

感谢您的帮助。

...even if you decide to use just the species column ignoring every other column.e. Species, location and cases to pivot wide, it still doesn't help.

实际上,只需很少的争论,它确实有帮助。

This is more complex than your comment appear to suggest.

我不相信是:

# load libraries
library(tidyverse)

# define data using the structure posted in the initial question

# create all_data_final_wider by taking all_data_final %>% remove all
# leading/trailing white space %>% convert cases column to lowercase %>% select
# columns to retain %>% remove exact duplicates %>% pivot from long to wide
all_data_final_wider <- all_data_final %>% 
  mutate_all(str_squish) %>% 
  mutate(cases = str_to_lower(cases)) %>% 
  select(Species, location, cases) %>% 
  distinct() %>% 
  pivot_wider(names_from = location, values_from = cases)

# prove that there are as many rows in all_data_final_wider as there are
# distinct spellings of the Species column
nrow(all_data_final_wider) == length(unique(all_data_final_wider$Species))
#> [1] TRUE

所以我坚持我的意见:

You'll need to fix these and all other inconsistencies in the input data if you expect to get sensible results from pivot_wider()

正如@hendrikvanb 指出的那样,您重复的输出行不仅是由于字符串造成的,而且还因为数据不完整以及某些输入字符串存在细微差异。即使两个字符串包含相同的人类信息 reader,R 也会将它们视为不同的,除非每个字符都相同。一旦我们解决了这个问题,解决方案就容易多了。

第 1 步:确保具有相似名称的条目具有相同的名称

以下代码以一些简单的整理开始(删除多余的白色 space,将所有内容设为小写)。然后它会在您的 table 中搜索相似的文本,并且对于每一对都会询问您是否要用另一个替换一个。

例如如果您的数据集包含 "levi et al. 1990" 和 "levi et al 1990",一个有句号,另一个没有,您将收到一条消息:

Do you want to replace "levi et al. 1990" with "levi et al 1990"?

你也会以相反的顺序被问到同样的问题。如果您单击 'yes',则第一个实例的所有实例将被数据库中的第二个实例替换。

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 步:保留可用的类别文本信息

这里的目标是确保如果该物种的一个记录有一个目、科、属或权威,那么它会出现在最后的 table 中。我们可以通过要求每个物种的最大值 order/family/genus 来做到这一点。

处理文本时,最多 return 按字母顺序排列最后一条记录。空白或白色 space 首先排序到顶部,因此我们必须使用 max 因为 min 将 return 清空文本字段。

此代码已合并到步骤 3。

第 3 步:尽可能保留大小写标记

通过将 case 列转换为数字,我们可以在寻找最大值 1 的情况下进行汇总。在某些情况下,NA 或 NULL 会被视为 -Inf,因此我们也会处理此问题。

以下代码解析同一 summarise_all 语句中的步骤 2 和步骤 3。

# 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))