First things first

  1. Crie um diretório de trabalho na raiz do seu computador. Evite muitos subdiretórios.
  2. É necessário também carregar os seguintes pacotes do CRAN: curl, rgbif, paleobioDB, devtools, tidyverse, readr, terra, taxsize, sf, string, rnaturalearth, countrycode e CoordinateCleaner;
  3. Instale e carregue também o pacote fossilbrush.

Para instalar os pacotes taxsize e CoordinateCleaner, carregue o pacote devtools e digite install_github('ropensci/taxize') e install_github('ropensci/CoordinateCleaner') porque esses pacotes só estão disponíveis no github atualmente.

Tutorial para fazer uma limpeza nos nomes baixados ("limpeza taxonômica")

1) A limpeza dependerá inteiramente da natureza dos seus dados. Se você estiver lidando com uma única espécie, então pode ser menos importante, embora você ainda precise verificar se há entradas desonestas. Para conjuntos de dados maiores e multi-táxons, o risco de erros de taxonomia aumenta. O primeiro passo é garantir que todos os nomes estejam formatados corretamente: uma palavra, além dos nomes das espécies, com a primeira letra maiúscula e todos os nomes livres de caracteres não alfabéticos. Se os nomes contiverem informações adicionais relevantes (por exemplo, subespécies), extraia esses dados para sua própria coluna. Essas etapas são todas muito fáceis de fazer individualmente.

                        # carregando a biblioteca:
                        library(stringr)
                        
                        # carregando um exemplo de uma lista de nomes de espécies de avaes do taxadb
                        bbs_species_list <- system.file("extdata/bbs.tsv", package = "taxadb")
                        bbs <- read.delim(bbs_species_list)

                        # fazendo todas as letras ficarem no formato caixa baixa, exceto a primeira que ficará em caixa alta: 
                        bbs$species <- str_to_sentence(str_to_lower(bbs$species))
                

2) Faremos algumas contagens rápidas de palavras em nomes. Essas funções destacam o uso do regex. A função grep() busca o vetor de nomes de gêneros para instâncias de espaços simples " ", ou seja, separadores de palavras. A função str_count() busca todas as instâncias de espaços em branco. Nesse caso, usaremos a sintaxe regex especial \s que é uma abreviação para espaços em branco.

                    # contando o número de palavras em cada nome usando diferentes tipos de métodos
                    grep(" ", bbs$genus)
                    str_count(bbs$species, pattern = "\\s")
                

No primeiro exemplo, grep() nos diz que dois elementos do vetor do gênero contêm espaços em branco, embora esperemos que esses nomes sejam palavras únicas. No segundo exemplo, st_count() relata contagens de espaços em branco. Ele nos diz que a maioria dos nomes de espécies contém um único espaço em branco, como esperado, mas não todos. Essas são as irregularidades que queremos capturar.

3) Também podemos usar regex para procurar por quaisquer caracteres que não sejam letras ou espaços em branco. Novamente, podemos ver alguma sintaxe especial: [] define um grupo de caracteres, enquanto ^ inverte o conjunto subsequente de caracteres, ou seja, corresponde a qualquer coisa que não esteja nos conjuntos de a a z, A a Z e espaços em branco. Podemos usar o resultado de grep para indexar também.

                    irreg <- grep("[^a-zA-Z ]", bbs$species)
                    bbs$species[irreg]
                  

4) Infelizmente, ainda descobrimos que há irregularidades. Em alguns casos, poderíamos simplesmente pegar as duas primeiras palavras, embora isso possa ser desaconselhável. Há uma razão pela qual esses nomes são dados da maneira que são: eles não são de identificações certas, então será realmente que podemos usá-los? Em outros casos, as duas primeiras palavras dariam um resultado inválido, por exemplo, a entrada 25 'Ardeid sp.' nem mesmo é classificada em nível de gênero. Por enquanto, rejeitaremos as anomalias que identificamos até agora e continuaremos a limpar.

                    bbs <- bbs[-grep(" ", bbs$genus),]
                    bbs <- bbs[str_count(bbs$species, pattern = "\\s") == 1,]
                    bbs <- bbs[-grep("[^a-zA-Z ]", bbs$species)]
                  

5) Uma vez que a formatação esteja harmonizada, você pode verificar se há alguma fonte taxonômica inválida. Novamente, isso dependerá inteiramente de quais dados você está usando, mas o pacote taxadb dá acesso a "esqueletos" de nomes em vários bancos de dados. Como os bancos de dados de ocorrência usam tabelas de taxonomia bem curadas, as chances são de que poucos erros sejam encontrados, mas isso ainda deve ser verificado usando a função get_ids().

                    # Carregando o pacote
                    library(taxadb)

                    # pegando os acertos
                    matches <- get_ids(bbs$species, "col")
                    bbs$species[which(is.na(matches))]
                  

6) Por fim, você pode usar métodos de sequências difusas para verificar discrepâncias de grafia. O pacote fossilbrush contém uma função para isso, onde os nomes são verificados entre si dentro de seus grupos taxonômicos maiores para reduzir o risco de correspondências falsas entre nomes semelhantes, mas genuinamente separados. Essa função também faz algumas verificações úteis, incluindo comprimentos de nomes corretos e caracteres não-letrados, mas também verifica se os táxons são atribuídos discretamente a táxons superiores únicos apenas.

                      # carregando o pacote
                      library(fossilbrush)
                      
                      scans <- check_taxonomy(bbs, ranks = c("order", "genus", "species"), species_sep = " ")
                      scans$synonyms
                    

Se examinarmos a verificação de sinônimos, podemos ver que algumas correspondências potenciais foram identificadas em alguns níveis taxonômicos diferentes. Essas correspondências ilustram os graus de similaridade de nomes que podemos encontrar, embora todos pareçam ser genuínos. Para ter certeza, tente obter IDs para todos os sinônimos potenciais (t1 e t2) em scans$synonyms. Será que todos são válidos?

Rotina para carregar e limpar seu conjunto de coordenadas.

Algumas questões para seu projeto de pesquisa:

  1. Quantos registros são potencialmente problemáticos?
  2. Quais são os principais problemas encontrados neles?
  3. Os registros obtidos no campo foram problemáticos? Se sim, o que aconteceu?
  4. A) A limpeza é cumprida muito bem pelo pacote CoordinateCleaner já que ele é configurado para usar padrões Darwin Core ao pesquisar nomes de colunas e, assim, é plug-and-play com qualquer conjunto de dados GBIF. É importante verificar seus dados num mapa para confirmar se tudo aparece como o esperado. As funções são configuradas para assumir que os registros sejam terrestres, verificando que pontos caem nos oceanos. Antes, vamos ver o que esse pacote faz em algumas das suas etapas:
                # carregando as bibliotecas:
                library(CoordinateCleaner)                        
                library(sf)
                library(rnaturalearth)
                library(devtools)
                library(tidyverse)
                library(countrycode)
                library(ggplot2)
    
                # 1) Leitura dos dados a partir de um arquivo .txt ou .csv ou .tsv
                dat_belos <- read_tsv("Belostomatidae.txt", guess_max = 25000, quote = "") # lendo, no máximo, 25 mil coordenadas
    
                # para ver o nome e quantidade de colunas:
                names(dat_belos)
                            
                # 2) algumas padronizações de nome de país e código e seleção das colunas que importam...
                dat_belos <- dat_belos %>%
                  dplyr::select(species, decimalLongitude, decimalLatitude, countryCode, individualCount,
                  gbifID, family, taxonRank, coordinateUncertaintyInMeters, year,
                  basisOfRecord,occurrenceStatus)%>%
                  mutate(countryCode = countrycode(dat_belos$countryCode, origin =  'iso2c', destination = 'iso3c'))
    
                # 3) visualizando as coordenadas num mapa
                world.inp <- map_data("world")
                p <- ggplot() +
                  geom_map(data = world.inp, map = world.inp, aes(x = long, y = lat, map_id = region), fill = "grey80")
                p <- p + geom_point(data = dat_belos, aes(x = decimalLongitude, y = decimalLatitude), size = 1)
                p <- p + coord_fixed() + theme_bw() + theme(axis.title = element_blank())
                p <- p + ggtitle("Belostomatidae (GBIF: 8,505 records)") # título
                p
    
                # 4) removendo registros sem coordenadas
                dat_clB <- dat_belos %>% filter(!is.na(decimalLongitude)) %>% filter(!is.na(decimalLatitude))
    
                # para ver quantos registros existem agora:
                dat_clB
                
                # 5) removendo registros de origem inadequada ou não aplicáveis
                dat_clB <- filter(dat_clB, basisOfRecord == "HUMAN_OBSERVATION" | basisOfRecord == "OBSERVATION" |
                  basisOfRecord == "PRESERVED_SPECIMEN" |  basisOfRecord == "MATERIAL_SAMPLE" |
                  basisOfRecord == "LIVING_SPECIMEN MACHINE_OBSERVATION" | is.na(basisOfRecord))
    
                # para ver quantos registros existem agora:
                dat_clB
    
                # 6) podemos excluir registros muito antigos...
                dat_clB <- dat_clB %>% filter(year > 1945) # antes da Segunda Grande Guerra
    
                # para ver quantos registros existem agora:
                dat_clB
    
                # 7) Só queremos incluir registros com identificação em nível de espécie
                dat_clB <- dat_clB %>% filter(taxonRank %in% "SPECIES" | is.na(taxonRank))
    
                # para ver quantos registros existem agora:
                dat_clB           
                
                # salvando o arquivo novo e (supostamente) limpo
                write_csv(dat_clB, "Belostomatidae_limpo.csv")
    
                    

    B) A limpeza agora será realizada de forma automatizada pelo pacote CoordinateCleaner. Será conferido se coordenadas caem nos oceanos, se existem coordenadas "zero", se existem coordenadas dentro de cidades, se existem dados discrepantes, se existem coordenadas dentro de institutos e museus etc.
                  # Dados de Panthera onca:
                  # 1) Leitura dos dados a partir de um arquivo .txt ou .csv ou .tsv
                  dat_teste <- read_tsv("onca_original.csv", guess_max = 25000, quote = "") # lendo, no máximo, 25 mil coordenadas
                  dat_teste
    
                  # Pronto! Agora, é só rodar...
                  flagsA <- clean_coordinates(dat_teste)
                  ##########################################################
                  ##########################################################
                  # de forma alternativa, você pode fazer isso:
                  dat_teste <- data.frame(dat_teste)
                  flagsB <- clean_coordinates(x = dat_teste, lon = "decimalLongitude", lat = "decimalLatitude", countries = "countryCode",
                                  species = "species", tests = c("equal", "gbif", "zeros",
                                  "seas"), seas_ref = buffland)
                ##########################################################
    
                # podemos ver os pontos que ficaram e os que foram retirados:
                plot(flagsA, lon = "decimalLongitude", lat = "decimalLatitude")
    
                # podemos ver quantos registros foram excluídos:
                sum(flagsA$.summary) # 6.091 pontos
    
                # Agora, é só remover dos dados originais:
                dat_teste_novo <- dat_teste[flagsA$.summary, ]
    
                # Salve o dado novo e limpo:
                write.csv(dat_teste_novo, 'panthera_occ_limpo.csv')
                

    C) Quando lidarmos com uma espécie marinha, precisaremos inverter manualmente os valores para que os registros dos oceanos passem, enquanto os dos continentes e ilhas não. Após isso, recalculamos o .summary .
                  flags <- flagsA %>%
                    mutate(.sea = !.sea) %>%
                    mutate(.summary = ifelse(rowSums(.[, 13:20]) == 8, TRUE, FALSE))
                  
                  sum(flags$.summary) # 850 registros
    
                  # podemos ver os pontos que ficaram e os que foram retirados:
                  plot(flags, lon = "decimalLongitude", lat = "decimalLatitude")
    
                  # Após a inspeção, podemos remover os registros de forma segura:
                  dat_oceano <- dat_clB[flags$.summary, ]
    
                  # Salve o dado novo e limpo:
                  write.csv(dat_oceano, 'Belos_occ_limpo_oceano.csv')