First things first
curl, rgbif, paleobioDB, devtools, tidyverse,
readr, terra, taxsize, sf, string, rnaturalearth, countrycode e CoordinateCleaner;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.
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?
Algumas questões para seu projeto de pesquisa:
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')