Microbioma de suelos.¶

Karol Salazar Barrantes.¶


Descripción del proyecto y justificación:¶

La metagenómica es una herramienta que permite conocer la diversidad de microorganismos y sus funciones, sin la necesidad de cultivarlos, considerando que solo un porcentaje muy pequeño se puede cultivar en un laboratorio. Esto permite analizar el microbioma de muestras ambientales, como el suelo. Los suelos se han investigado siempre en función de sus características físicas y químicas; pero, hasta hace algunos años se han reportado estudios que involucran la comprensión de la dinámica de los microorganismos del suelo. Realizar estudios metagenómicos de una muestra ambiental involucra analizar gran cantidad de secuencias de material genético, por lo que se necesita implementar métodos para lograr sacar el mayor provecho a esa información, por ejemplo, por medio de bibliotecas de Python. Este proyecto consiste en descargar un dataset con información de microbioma de suelos de Finlandia, para filtrar la información más importante, analizarla y comparar los datos con resultados obtenidos en un proyecto de investigación propia.

Antecedentes:¶

El análisis de las características, taxonomía y ecología de los microorganismos en muestras ambientales, se ha logrado de manera más eficiente mediante los avances en la secuenciación y la bioinformática. Estos conocimientos pueden ser una herramienta para brindar puntuaciones de calidad del suelo, como se muestra en los resultados obtenidos por Hermans et al. (2020), en donde se analizaron 3000 muestras de suelo de 606 sitios de Nueva Zelanda. Además, en este tipo de investigaciones, no solo es importante la información obtenida a partir de las secuencias del material genético, por el contrario, se vuelve más informativa en conjunto con metadatos, que incluyan descripciones, características y otros tipos de procesamientos de muestras, por ejemplo, datos de análisis fisicoquímicos, condiciones ambientales, tipo de muestra, tratamientos, entre otros. Un estudio realizado en diferentes tipos de usos de suelos de países europeos, determinó diferencias significativas en las estructuras de la comunidad de bacterias y que es explicado en gran parte por las propiedades del suelo en relación con su pH, contenido total de carbón y textura (Plassart et al., 2019). La ciencia de datos permite filtrar y limpiar las secuencias de ADN, transformar esa información mediante bases de datos, analizar, visualizar los datos y aplicar estadísticas. Algunos de los métodos utilizados por Berihu et al. (2023), en función de seleccionar taxones beneficiosos del suelo para los cultivos, fueron filtrar los datos de la secuenciación, eliminando secuencias de baja calidad. Después, se utilizó una base de datos de proteínas de NCBI (National Center for Biotechnology Information) para asignar anotaciones de funciones. Con estos datos se desarrolló un método de simulación de las actividades metabólicas. Además, se aplicó estadística y se visualizaron los datos mediante gráficos de análisis de coordenadas principales. Los aportes a la información generada sobre el microbioma del suelo, como se mencionó anteriormente, puede ser por medio de análisis de un conjunto de muestras de un estudio en específico, pero también puede estar enfocado en crear bases de datos a partir de otras bases de datos, para de esta manera tener referencias específicas del suelo. Edwin et al. (2024), unieron información de bases de datos de NCBI y de RefSoil, para simular una comunidad, mediante genomas que son comunes en ecosistemas del suelo.

Descipción del problema:¶

¿Es posible filtrar la información del dataset "Soil metagenome from healthy and unhealthy agricultural soil" para analizar, comparar y visualizar los datos más relevantes?

Objetivo:¶

Analizar el dataset "Soil metagenome from healthy and unhealthy agricultural soil", mediante bibliotecas de aprendizaje automático, para la visualización de los taxones más relevantes y la comparación con otro estudio de metagenómica de suelos.

Descipción del conjunto de datos:¶

El dataset "Soil metagenome from healthy and unhealthy agricultural soil" (MGnify, 2020), es un conjunto de datos sobre microbioma de suelos agrícolas de Finlandia, considerados saludables y no saludables, publicado en GBIF (Global Biodiversity Information Facility) el 04 de febrero del 2020 por la Universidad de Helsinki.

El mapa de Finlandia fue descargado en: gadm.org

La información de los grupos taxonómicos para realizar la comparación corresponden a los resultados de una investigación (mi tesis en desarrollo) sobre bacterias de suelos de cafetales de Naranjo, que son considerados de calidad Baja y Media para el cultivo del café, según la zonificación agroecológica del Instituto Nacional de Tecnología Agropecuaria (INTA, 2019). Mediante los análisis de la secuenciación de ADN de muestras de suelos, estos grupos taxonómicos son considerados bioindicadores de la calidad Baja y Media (p<0.05).


Código¶

In [1]:
!pip install pandas
!pip install ydata-profiling

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from ydata_profiling import ProfileReport

!pip install geodatasets
import geopandas as gpd
import geodatasets
Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (2.0.3)
Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas) (2023.4)
Requirement already satisfied: tzdata>=2022.1 in /usr/local/lib/python3.10/dist-packages (from pandas) (2024.1)
Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas) (1.25.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)
Collecting ydata-profiling
  Downloading ydata_profiling-4.8.3-py2.py3-none-any.whl (359 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 359.5/359.5 kB 2.9 MB/s eta 0:00:00
Requirement already satisfied: scipy<1.14,>=1.4.1 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (1.11.4)
Requirement already satisfied: pandas!=1.4.0,<3,>1.1 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (2.0.3)
Requirement already satisfied: matplotlib<3.9,>=3.2 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (3.7.1)
Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (2.7.2)
Requirement already satisfied: PyYAML<6.1,>=5.0.0 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (6.0.1)
Requirement already satisfied: jinja2<3.2,>=2.11.1 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (3.1.4)
Collecting visions[type_image_path]<0.7.7,>=0.7.5 (from ydata-profiling)
  Downloading visions-0.7.6-py3-none-any.whl (104 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 104.8/104.8 kB 8.6 MB/s eta 0:00:00
Requirement already satisfied: numpy<2,>=1.16.0 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (1.25.2)
Collecting htmlmin==0.1.12 (from ydata-profiling)
  Downloading htmlmin-0.1.12.tar.gz (19 kB)
  Preparing metadata (setup.py) ... done
Collecting phik<0.13,>=0.11.1 (from ydata-profiling)
  Downloading phik-0.12.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (686 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 686.1/686.1 kB 35.4 MB/s eta 0:00:00
Requirement already satisfied: requests<3,>=2.24.0 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (2.31.0)
Requirement already satisfied: tqdm<5,>=4.48.2 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (4.66.4)
Requirement already satisfied: seaborn<0.14,>=0.10.1 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (0.13.1)
Collecting multimethod<2,>=1.4 (from ydata-profiling)
  Downloading multimethod-1.11.2-py3-none-any.whl (10 kB)
Requirement already satisfied: statsmodels<1,>=0.13.2 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (0.14.2)
Collecting typeguard<5,>=3 (from ydata-profiling)
  Downloading typeguard-4.3.0-py3-none-any.whl (35 kB)
Collecting imagehash==4.3.1 (from ydata-profiling)
  Downloading ImageHash-4.3.1-py2.py3-none-any.whl (296 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 296.5/296.5 kB 26.7 MB/s eta 0:00:00
Requirement already satisfied: wordcloud>=1.9.1 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (1.9.3)
Collecting dacite>=1.8 (from ydata-profiling)
  Downloading dacite-1.8.1-py3-none-any.whl (14 kB)
Requirement already satisfied: numba<1,>=0.56.0 in /usr/local/lib/python3.10/dist-packages (from ydata-profiling) (0.58.1)
Requirement already satisfied: PyWavelets in /usr/local/lib/python3.10/dist-packages (from imagehash==4.3.1->ydata-profiling) (1.6.0)
Requirement already satisfied: pillow in /usr/local/lib/python3.10/dist-packages (from imagehash==4.3.1->ydata-profiling) (9.4.0)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2<3.2,>=2.11.1->ydata-profiling) (2.1.5)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (1.2.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (4.52.4)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (1.4.5)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (24.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (3.1.2)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata-profiling) (2.8.2)
Requirement already satisfied: llvmlite<0.42,>=0.41.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba<1,>=0.56.0->ydata-profiling) (0.41.1)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas!=1.4.0,<3,>1.1->ydata-profiling) (2023.4)
Requirement already satisfied: tzdata>=2022.1 in /usr/local/lib/python3.10/dist-packages (from pandas!=1.4.0,<3,>1.1->ydata-profiling) (2024.1)
Requirement already satisfied: joblib>=0.14.1 in /usr/local/lib/python3.10/dist-packages (from phik<0.13,>=0.11.1->ydata-profiling) (1.4.2)
Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->ydata-profiling) (0.7.0)
Requirement already satisfied: pydantic-core==2.18.3 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->ydata-profiling) (2.18.3)
Requirement already satisfied: typing-extensions>=4.6.1 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->ydata-profiling) (4.12.0)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata-profiling) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata-profiling) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata-profiling) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata-profiling) (2024.2.2)
Requirement already satisfied: patsy>=0.5.6 in /usr/local/lib/python3.10/dist-packages (from statsmodels<1,>=0.13.2->ydata-profiling) (0.5.6)
Requirement already satisfied: attrs>=19.3.0 in /usr/local/lib/python3.10/dist-packages (from visions[type_image_path]<0.7.7,>=0.7.5->ydata-profiling) (23.2.0)
Requirement already satisfied: networkx>=2.4 in /usr/local/lib/python3.10/dist-packages (from visions[type_image_path]<0.7.7,>=0.7.5->ydata-profiling) (3.3)
Requirement already satisfied: six in /usr/local/lib/python3.10/dist-packages (from patsy>=0.5.6->statsmodels<1,>=0.13.2->ydata-profiling) (1.16.0)
Building wheels for collected packages: htmlmin
  Building wheel for htmlmin (setup.py) ... done
  Created wheel for htmlmin: filename=htmlmin-0.1.12-py3-none-any.whl size=27080 sha256=cd49fc6872afe215b0bdbdfe1324dcda01871ab78472d8f77cdd995274eb6c56
  Stored in directory: /root/.cache/pip/wheels/dd/91/29/a79cecb328d01739e64017b6fb9a1ab9d8cb1853098ec5966d
Successfully built htmlmin
Installing collected packages: htmlmin, typeguard, multimethod, dacite, imagehash, visions, phik, ydata-profiling
Successfully installed dacite-1.8.1 htmlmin-0.1.12 imagehash-4.3.1 multimethod-1.11.2 phik-0.12.4 typeguard-4.3.0 visions-0.7.6 ydata-profiling-4.8.3
Collecting geodatasets
  Downloading geodatasets-2023.12.0-py3-none-any.whl (19 kB)
Requirement already satisfied: pooch in /usr/local/lib/python3.10/dist-packages (from geodatasets) (1.8.1)
Requirement already satisfied: platformdirs>=2.5.0 in /usr/local/lib/python3.10/dist-packages (from pooch->geodatasets) (4.2.2)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from pooch->geodatasets) (24.0)
Requirement already satisfied: requests>=2.19.0 in /usr/local/lib/python3.10/dist-packages (from pooch->geodatasets) (2.31.0)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19.0->pooch->geodatasets) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19.0->pooch->geodatasets) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19.0->pooch->geodatasets) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.19.0->pooch->geodatasets) (2024.2.2)
Installing collected packages: geodatasets
Successfully installed geodatasets-2023.12.0
In [2]:
from google.colab import drive
drive.mount('/content/drive')
df = pd.read_csv('/content/drive/MyDrive/soil_metagenome.csv', delimiter='\t') # delimiter='\t' para sets separados por tabulaciones
Mounted at /content/drive
In [ ]:
# Sumar valores faltantes
df.isna().sum()
Out[ ]:
gbifID                                  0
datasetKey                              0
occurrenceID                            0
kingdom                                 0
phylum                               1835
class                                3300
order                                4953
family                               8101
genus                               13247
species                             22109
infraspecificEpithet                25560
taxonRank                               0
scientificName                          0
verbatimScientificName                  0
verbatimScientificNameAuthorship    25560
countryCode                             0
locality                            25560
stateProvince                       25560
occurrenceStatus                        0
individualCount                     25560
publishingOrgKey                        0
decimalLatitude                         0
decimalLongitude                        0
coordinateUncertaintyInMeters       25560
coordinatePrecision                 25560
elevation                           25560
elevationAccuracy                   25560
depth                               25560
depthAccuracy                       25560
eventDate                               0
day                                     0
month                                   0
year                                    0
taxonKey                                0
speciesKey                          22109
basisOfRecord                           0
institutionCode                     25560
collectionCode                      25560
catalogNumber                       25560
recordNumber                        25560
identifiedBy                        25560
dateIdentified                      25560
license                                 0
rightsHolder                        25560
recordedBy                          25560
typeStatus                          25560
establishmentMeans                  25560
lastInterpreted                         0
mediaType                           25560
issue                                   0
dtype: int64
In [ ]:
# Determinar la forma
df.shape
Out[ ]:
(25560, 50)

Filtración

In [ ]:
#----- Primer filtro -----

# Eliminar las columnas que todos sus valores sean NaN (25560)
print("Antes:", df.columns, "\n")

df = df.dropna(axis=1, how='all')

print("Después:", df.columns)
Antes: Index(['gbifID', 'datasetKey', 'occurrenceID', 'kingdom', 'phylum', 'class',
       'order', 'family', 'genus', 'species', 'infraspecificEpithet',
       'taxonRank', 'scientificName', 'verbatimScientificName',
       'verbatimScientificNameAuthorship', 'countryCode', 'locality',
       'stateProvince', 'occurrenceStatus', 'individualCount',
       'publishingOrgKey', 'decimalLatitude', 'decimalLongitude',
       'coordinateUncertaintyInMeters', 'coordinatePrecision', 'elevation',
       'elevationAccuracy', 'depth', 'depthAccuracy', 'eventDate', 'day',
       'month', 'year', 'taxonKey', 'speciesKey', 'basisOfRecord',
       'institutionCode', 'collectionCode', 'catalogNumber', 'recordNumber',
       'identifiedBy', 'dateIdentified', 'license', 'rightsHolder',
       'recordedBy', 'typeStatus', 'establishmentMeans', 'lastInterpreted',
       'mediaType', 'issue'],
      dtype='object') 

Después: Index(['gbifID', 'datasetKey', 'occurrenceID', 'kingdom', 'phylum', 'class',
       'order', 'family', 'genus', 'species', 'taxonRank', 'scientificName',
       'verbatimScientificName', 'countryCode', 'occurrenceStatus',
       'publishingOrgKey', 'decimalLatitude', 'decimalLongitude', 'eventDate',
       'day', 'month', 'year', 'taxonKey', 'speciesKey', 'basisOfRecord',
       'license', 'lastInterpreted', 'issue'],
      dtype='object')
In [ ]:
# Creamos el informe
nombre = "Soil microbiome"
profile = ProfileReport(df, title=nombre, explorative=True)

# Mostrar el informe en un notebook
profile.to_notebook_iframe()
Output hidden; open in https://colab.research.google.com to view.
In [ ]:
# Descargar el .htlm en la sección de carpetas
profile.to_file("Reporte.htlm")
/usr/local/lib/python3.10/dist-packages/ydata_profiling/profile_report.py:363: UserWarning: Try running command: 'pip install --upgrade Pillow' to avoid ValueError
  warnings.warn(
/usr/local/lib/python3.10/dist-packages/ydata_profiling/profile_report.py:384: UserWarning: Extension .htlm not supported. For now we assume .html was intended. To remove this warning, please use .html or .json.
  warnings.warn(
Export report to file:   0%|          | 0/1 [00:00<?, ?it/s]
In [ ]:
#----- Segundo filtro -----

# Filtrar las columnas con información importante y diferente

print("Antes:", df.columns, "\n")

df = df[['gbifID', 'datasetKey', 'occurrenceID', 'kingdom', 'phylum', 'class',
       'order', 'family', 'genus', 'species', 'taxonRank', 'scientificName',
       'countryCode', 'decimalLatitude', 'decimalLongitude',
         'eventDate', 'taxonKey', 'speciesKey', 'issue']]

print("Después:", df.columns)
Antes: Index(['gbifID', 'datasetKey', 'occurrenceID', 'kingdom', 'phylum', 'class',
       'order', 'family', 'genus', 'species', 'taxonRank', 'scientificName',
       'verbatimScientificName', 'countryCode', 'occurrenceStatus',
       'publishingOrgKey', 'decimalLatitude', 'decimalLongitude', 'eventDate',
       'day', 'month', 'year', 'taxonKey', 'speciesKey', 'basisOfRecord',
       'license', 'lastInterpreted', 'issue'],
      dtype='object') 

Después: Index(['gbifID', 'datasetKey', 'occurrenceID', 'kingdom', 'phylum', 'class',
       'order', 'family', 'genus', 'species', 'taxonRank', 'scientificName',
       'countryCode', 'decimalLatitude', 'decimalLongitude', 'eventDate',
       'taxonKey', 'speciesKey', 'issue'],
      dtype='object')
In [3]:
df.head(3)
Out[3]:
gbifID datasetKey occurrenceID kingdom phylum class order family genus species ... identifiedBy dateIdentified license rightsHolder recordedBy typeStatus establishmentMeans lastInterpreted mediaType issue
0 3916717560 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198885_Bacteria::Verrucomicrobia:Opituta... Bacteria Verrucomicrobiota Verrucomicrobiae Opitutales Opitutaceae Opitutus NaN ... NaN NaN CC_BY_4_0 NaN NaN NaN NaN 2024-05-18T16:56:01.993Z NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED...
1 3916717559 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198894_Bacteria::Proteobacteria:Zetaprot... Bacteria Proteobacteria Zetaproteobacteria Mariprofundales Mariprofundaceae Mariprofundus NaN ... NaN NaN CC_BY_4_0 NaN NaN NaN NaN 2024-05-18T16:56:01.993Z NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED...
2 3916717558 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198886_Bacteria::Actinobacteria:Actinoba... Bacteria Actinobacteriota Actinomycetia Actinomycetales Microbacteriaceae Leifsonia NaN ... NaN NaN CC_BY_4_0 NaN NaN NaN NaN 2024-05-18T16:56:05.146Z NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED...

3 rows × 50 columns

Categorización

In [ ]:
# Crear una función para categorizar según el lugar de muestreo

def categorizar(fila):
  ID = fila["decimalLatitude"]

  if ID == 60.746:
    return "tipo_1"
  elif ID == 60.559:
    return "tipo_2"
  elif ID == 60.561:
    return "tipo_2"
  else:
    return "tipo_3"

# Aplicar la función a cada fila y agregar una nueva columna
df["Categoría"] = df.apply(categorizar, axis=1)
df
Out[ ]:
gbifID datasetKey occurrenceID kingdom phylum class order family genus species taxonRank scientificName countryCode decimalLatitude decimalLongitude eventDate taxonKey speciesKey issue Categoría
0 3916717560 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198885_Bacteria::Verrucomicrobia:Opituta... Bacteria Verrucomicrobiota Verrucomicrobiae Opitutales Opitutaceae Opitutus NaN GENUS Opitutus Chin et al., 2001 FI 60.746 21.689 2016-07-25 3229020 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1
1 3916717559 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198894_Bacteria::Proteobacteria:Zetaprot... Bacteria Proteobacteria Zetaproteobacteria Mariprofundales Mariprofundaceae Mariprofundus NaN GENUS Mariprofundus Emerson et al., 2010 FI 60.425 23.000 2016-07-25 5905993 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_3
2 3916717558 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198886_Bacteria::Actinobacteria:Actinoba... Bacteria Actinobacteriota Actinomycetia Actinomycetales Microbacteriaceae Leifsonia NaN GENUS Leifsonia Evtushenko et al., 2000 FI 60.425 23.000 2016-07-25 8159185 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_3
3 3916717557 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198897_Bacteria::Actinobacteria:Nitrilir... Bacteria Actinobacteriota Actinomycetia NaN NaN NaN NaN CLASS Nitriliruptoria FI 60.426 22.992 2016-07-25 10900290 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_3
4 3916717556 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198890_Bacteria::Actinobacteria:Actinoba... Bacteria Actinobacteriota Actinomycetia Mycobacteriales Pseudonocardiaceae Allokutzneria Allokutzneria albata SPECIES Allokutzneria albata (Tomita et al., 1993) Lab... FI 60.559 22.640 2016-07-25 8374936 8374936.0 GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_2
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
25555 2441745103 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198881_Bacteria::Acidobacteria:Solibacte... Bacteria Acidobacteriota NaN NaN NaN NaN NaN PHYLUM Acidobacteria FI 60.746 21.689 2016-07-25 25 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1
25556 2441745092 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198881_Bacteria::Planctomycetes:Planctom... Bacteria Planctomycetota Planctomycetia Planctomycetales NaN NaN NaN ORDER Planctomycetales FI 60.746 21.689 2016-07-25 1218 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1
25557 2441744962 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198881_Bacteria::Actinobacteria:::::Acti... Bacteria Actinobacteriota NaN NaN NaN NaN NaN PHYLUM Actinobacteriota FI 60.746 21.689 2016-07-25 10813635 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1
25558 2441744626 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198881_Bacteria:::::::bacterium_enrichme... Bacteria NaN NaN NaN NaN NaN NaN KINGDOM Bacteria FI 60.746 21.689 2016-07-25 3 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1
25559 2441744624 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198881_Bacteria::Acidobacteria:Blastocat... Bacteria Acidobacteriota Blastocatellia Pyrinomonadales Blastocatellaceae Aridibacter NaN GENUS Aridibacter Huber et al., 2014 FI 60.746 21.689 2016-07-25 8427336 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1

25560 rows × 20 columns

Mapas

In [ ]:
# Cargar el mapa de Finlandia

fi_mapa = gpd.read_file('gadm41_FIN_2.shp')
fi_mapa
Out[ ]:
geometry
0 POLYGON ((29.62012 61.93353, 29.62154 61.93797...
1 POLYGON ((28.05437 62.28169, 28.04478 62.28224...
2 POLYGON ((26.13442 61.15151, 26.12514 61.15193...
3 POLYGON ((26.50956 61.29203, 26.50025 61.29247...
4 MULTIPOLYGON (((24.19583 65.60833, 24.19792 65...
5 POLYGON ((29.12566 63.84394, 29.11557 63.84456...
6 MULTIPOLYGON (((25.24760 63.60816, 25.23753 63...
7 MULTIPOLYGON (((25.22448 60.12656, 25.22448 60...
8 MULTIPOLYGON (((27.39792 60.30417, 27.39636 60...
9 POLYGON ((26.21460 60.75100, 26.21373 60.74652...
10 POLYGON ((27.98806 60.66637, 27.97896 60.66691...
11 MULTIPOLYGON (((22.96194 59.75264, 22.96194 59...
12 POLYGON ((26.30343 61.62195, 26.30360 61.62281...
13 MULTIPOLYGON (((24.17770 63.14720, 24.16798 63...
14 MULTIPOLYGON (((22.07083 59.73736, 22.07139 59...
15 MULTIPOLYGON (((21.29427 61.97760, 21.29427 61...
16 POLYGON ((25.40037 60.84913, 25.39961 60.84466...
17 MULTIPOLYGON (((23.77942 61.05702, 23.77888 61...
18 MULTIPOLYGON (((21.41042 61.06250, 21.40833 61...
19 POLYGON ((21.63754 62.02965, 21.63746 62.02965...
20 POLYGON ((24.59601 60.65826, 24.59537 60.65378...
In [ ]:
# Generar el mapa por regiones

fi_mapa.plot(figsize = (8,8), edgecolor="black")
Out[ ]:
<Axes: >
No description has been provided for this image
In [ ]:
# Convertir las coordenadas
obvs_points = gpd.points_from_xy(df.decimalLongitude, df.decimalLatitude)

#Crear un geodataframe en base a un dataframe
obvs_gdf = gpd.GeoDataFrame(df, geometry=obvs_points)
obvs_gdf.head()
Out[ ]:
gbifID datasetKey occurrenceID kingdom phylum class order family genus species ... scientificName countryCode decimalLatitude decimalLongitude eventDate taxonKey speciesKey issue Categoría geometry
0 3916717560 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198885_Bacteria::Verrucomicrobia:Opituta... Bacteria Verrucomicrobiota Verrucomicrobiae Opitutales Opitutaceae Opitutus NaN ... Opitutus Chin et al., 2001 FI 60.746 21.689 2016-07-25 3229020 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_1 POINT (21.68900 60.74600)
1 3916717559 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198894_Bacteria::Proteobacteria:Zetaprot... Bacteria Proteobacteria Zetaproteobacteria Mariprofundales Mariprofundaceae Mariprofundus NaN ... Mariprofundus Emerson et al., 2010 FI 60.425 23.000 2016-07-25 5905993 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_3 POINT (23.00000 60.42500)
2 3916717558 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198886_Bacteria::Actinobacteria:Actinoba... Bacteria Actinobacteriota Actinomycetia Actinomycetales Microbacteriaceae Leifsonia NaN ... Leifsonia Evtushenko et al., 2000 FI 60.425 23.000 2016-07-25 8159185 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_3 POINT (23.00000 60.42500)
3 3916717557 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198897_Bacteria::Actinobacteria:Nitrilir... Bacteria Actinobacteriota Actinomycetia NaN NaN NaN NaN ... Nitriliruptoria FI 60.426 22.992 2016-07-25 10900290 NaN GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_3 POINT (22.99200 60.42600)
4 3916717556 7585b2f0-0150-4409-985d-7b57832e3dca MGYA00198890_Bacteria::Actinobacteria:Actinoba... Bacteria Actinobacteriota Actinomycetia Mycobacteriales Pseudonocardiaceae Allokutzneria Allokutzneria albata ... Allokutzneria albata (Tomita et al., 1993) Lab... FI 60.559 22.640 2016-07-25 8374936 8374936.0 GEODETIC_DATUM_ASSUMED_WGS84;CONTINENT_DERIVED... tipo_2 POINT (22.64000 60.55900)

5 rows × 21 columns

In [ ]:
# Filtrar observaciones por calidad
filtered_1 = obvs_gdf[obvs_gdf['Categoría'] == 'tipo_1']
filtered_2 = obvs_gdf[obvs_gdf['Categoría'] == 'tipo_2']
filtered_3 = obvs_gdf[obvs_gdf['Categoría'] == 'tipo_3']


# Lienzo
prov_map = fi_mapa
lienzo = prov_map.plot(
    figsize=(10, 10),
    color="lightgreen",
    edgecolor="black",
    linewidth=0.5
)

# Título
title = "Zonas muestreadas en Turku, Finlandia"
lienzo.set_title(title)

# Ejes
lienzo.set_xlabel("Longitud")
lienzo.set_ylabel("Latitud")

# Calidades
filtered_1.plot(ax=lienzo, color="red", label='Tipo 1', markersize=40)
filtered_2.plot(ax=lienzo, color="blue", label='Tipo 2', markersize=40)
filtered_3.plot(ax=lienzo, color="green", label='Tipo 3', markersize=40)


# Leyenda
lienzo.legend()

# Ver gráfico
plt.show()
No description has been provided for this image

Bioindicadores de calidad del suelo de cafetales de Naranjo, Costa Rica.

------ Filos ------
Calidad Baja: Chloroflexota, GAL15.
Calidad Media: Actinobacteriota, Proteobacteria.

------ Clases ------
Calidad Baja: Acidobacteriae, TK10.
Calidad Media: Actinomycetia, Gitt-GS-136.\

------ Géneros ------
Calidad Baja: HSB OF53-F07, Acidibacter.
Calidad Media: mle1-7, Hyphomicrobium, Pedomicrobium, Gaiella. \


Filos

In [ ]:
# Calcular la matriz de contingencia para Filo, muestra el conteo de combinaciones entre dos variables
contingencia = pd.crosstab(df['Categoría'], df['phylum'])

# Convertirla en una matriz de proporciones
proporciones = contingencia.div(contingencia.sum(axis=1), axis=0)

# Calcular la diferencia de proporciones
diferencias = proporciones.iloc[0] - proporciones.iloc[1]

# Seleccionar los 5 valores con mayor diferencia absoluta
top_5_diferencias = diferencias.abs().nlargest(5).index
print("Los 5 filos más distintos son:", top_5_diferencias)
Los 5 filos más distintos son: Index(['Ascomycota', 'Proteobacteria', 'Ciliophora', 'Actinobacteriota',
       'Verrucomicrobiota'],
      dtype='object', name='phylum')
In [ ]:
# Filtrar el DataFrame
g_1 = df[df['phylum'] == 'Ascomycota']
g_2 = df[df['phylum'] == 'Proteobacteria']
g_3 = df[df['phylum'] == 'Ciliophora']
g_4 = df[df['phylum'] == 'Actinobacteriota']
g_5 = df[df['phylum'] == 'Verrucomicrobiota']

# Contar la cantidad en cada categoría
conteo_1 = g_1.groupby("Categoría").size()
conteo_2 = g_2.groupby("Categoría").size()
conteo_3 = g_3.groupby("Categoría").size()
conteo_4 = g_4.groupby("Categoría").size()
conteo_5 = g_5.groupby("Categoría").size()

# Combinar los conteos en un solo DataFrame
conteo_combinado = pd.concat([conteo_1, conteo_2, conteo_3, conteo_4, conteo_5], axis=1)
 # Renombrar las columnas
conteo_combinado.columns = ['Ascomycota', 'Proteobacteria', 'Ciliophora', 'Actinobacteriota', 'Verrucomicrobiota']

# Tamaño del gráfico
plt.figure(figsize=(10, 20))

# Crear gráfico de barras horizontal
conteo_combinado.plot(kind='barh', color=["#a0d6b4", "#add8e6", "#ffd700", "#ffb6c1", "#dda0dd"])

# Título
plt.title('Frecuencia de los filos más distintos entre categorías')

# Etiqueta del eje Y
plt.ylabel('Categoría')

# Etiqueta del eje X
plt.xlabel('Frecuencia')

# Añadir cuadrícula
plt.grid()

# Mostrar leyenda fuera del gráfico
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# Ajustar diseño para que la leyenda no se corte
plt.tight_layout()

# Mostrar gráfico
plt.show()
<Figure size 1000x2000 with 0 Axes>
No description has been provided for this image

------ Filos de Naranjo ------
Calidad Baja: Chloroflexota, GAL15.
Calidad Media: Actinobacteriota, Proteobacteria


Clases

In [ ]:
# Calcular la matriz de contingencia para Clase
contingencia = pd.crosstab(df['Categoría'], df['class'])

# Convertirla en una matriz de proporciones
proporciones = contingencia.div(contingencia.sum(axis=1), axis=0)

# Calcular la diferencia de proporciones
diferencias = proporciones.iloc[0] - proporciones.iloc[1]

# Seleccionar los 5 valores con mayor diferencia absoluta
top_5_diferencias = diferencias.abs().nlargest(5).index
print("Las 5 clases más distintos son:", top_5_diferencias)
Las 5 clases más distintos son: Index(['Actinomycetia', 'Alphaproteobacteria', 'Sordariomycetes',
       'Verrucomicrobiae', 'Gammaproteobacteria'],
      dtype='object', name='class')
In [ ]:
# Filtrar el DataFrame
g_1 = df[df['class'] == 'Actinomycetia']
g_2 = df[df['class'] == 'Alphaproteobacteria']
g_3 = df[df['class'] == 'Sordariomycetes']
g_4 = df[df['class'] == 'Verrucomicrobiae']
g_5 = df[df['class'] == 'Gammaproteobacteria']

# Contar la cantidad en cada categoría
conteo_1 = g_1.groupby("Categoría").size()
conteo_2 = g_2.groupby("Categoría").size()
conteo_3 = g_3.groupby("Categoría").size()
conteo_4 = g_4.groupby("Categoría").size()
conteo_5 = g_5.groupby("Categoría").size()

# Combinar los conteos en un solo DataFrame
conteo_combinado = pd.concat([conteo_1, conteo_2, conteo_3, conteo_4, conteo_5], axis=1)
 # Renombrar las columnas
conteo_combinado.columns = ['Actinomycetia', 'Alphaproteobacteria', 'Sordariomycetes', 'Verrucomicrobiae', 'Gammaproteobacteria']

# Tamaño del gráfico
plt.figure(figsize=(10, 20))

# Crear gráfico de barras horizontal
conteo_combinado.plot(kind='barh', color=["#a0d6b4", "#add8e6", "#ffd700", "#ffb6c1", "#dda0dd"])

# Título
plt.title('Frecuencia de las clases más distintas entre categorías')

# Etiqueta del eje Y
plt.ylabel('Categoría')

# Etiqueta del eje X
plt.xlabel('Frecuencia')

# Añadir cuadrícula
plt.grid()

# Mostrar leyenda fuera del gráfico
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# Ajustar diseño para que la leyenda no se corte
plt.tight_layout()

# Mostrar gráfico
plt.show()
<Figure size 1000x2000 with 0 Axes>
No description has been provided for this image

------ Clases de Naranjo ------
Calidad Baja: Acidobacteriae, TK10.
Calidad Media: Actinomycetia, Gitt-GS-136.


Géneros

In [ ]:
# Calcular la matriz de contingencia para Género
contingencia = pd.crosstab(df['Categoría'], df['genus'])

# Convertirla en una matriz de proporciones
proporciones = contingencia.div(contingencia.sum(axis=1), axis=0)

# Calcular la diferencia de proporciones
diferencias = proporciones.iloc[0] - proporciones.iloc[1]

# Seleccionar los 5 valores con mayor diferencia absoluta
top_5_diferencias = diferencias.abs().nlargest(5).index
print("Los 5 géneros más distintos son:", top_5_diferencias)
Los 5 géneros más distintos son: Index(['Arthrobacter', 'Devosia', 'Ktedonobacter', 'Streptosporangium',
       'Actinoplanes'],
      dtype='object', name='genus')
In [ ]:
# Filtrar el DataFrame
g_1 = df[df['genus'] == 'Arthrobacter']
g_2 = df[df['genus'] == 'Devosia']
g_3 = df[df['genus'] == 'Ktedonobacter']
g_4 = df[df['genus'] == 'Streptosporangium']
g_5 = df[df['genus'] == 'Actinoplanes']

# Contar la cantidad en cada categoría
conteo_1 = g_1.groupby("Categoría").size()
conteo_2 = g_2.groupby("Categoría").size()
conteo_3 = g_3.groupby("Categoría").size()
conteo_4 = g_4.groupby("Categoría").size()
conteo_5 = g_5.groupby("Categoría").size()

# Combinar los conteos en un solo DataFrame
conteo_combinado = pd.concat([conteo_1, conteo_2, conteo_3, conteo_4, conteo_5], axis=1)
 # Renombrar las columnas
conteo_combinado.columns = ['Arthrobacter', 'Devosia', 'Ktedonobacter', 'Streptosporangium', 'Actinoplanes']

# Tamaño del gráfico
plt.figure(figsize=(10, 20))

# Crear gráfico de barras horizontal
conteo_combinado.plot(kind='barh', color=["#a0d6b4", "#add8e6", "#ffd700", "#ffb6c1", "#dda0dd"])

# Título
plt.title('Frecuencia de los géneros más distintos entre categorías')

# Etiqueta del eje Y
plt.ylabel('Categoría')

# Etiqueta del eje X
plt.xlabel('Frecuencia')

# Añadir cuadrícula
plt.grid()

# Mostrar leyenda fuera del gráfico
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# Ajustar diseño para que la leyenda no se corte
plt.tight_layout()

# Mostrar gráfico
plt.show()
<Figure size 1000x2000 with 0 Axes>
No description has been provided for this image

------ Géneros de Naranjo ------
Calidad Baja: HSB OF53-F07, Acidibacter.
Calidad Media: mle1-7, Hyphomicrobium, Pedomicrobium, Gaiella. \

In [ ]:
# Filtrar el DataFrame por los géneros de Naranjo con mayor diferencia entre categorías.
aci_g = df[df['genus'] == 'Acidibacter']
hyp_g = df[df['genus'] == 'Hyphomicrobium']

# Contar la cantidad en cada categoría
conteo_aci_g = aci_g.groupby("Categoría").size()
conteo_hyp_g = hyp_g.groupby("Categoría").size()

# Combinar los conteos en un solo DataFrame
conteo_combinado = pd.concat([conteo_aci_g, conteo_hyp_g], axis=1)
 # Renombrar las columnas
conteo_combinado.columns = ['Acidibacter', 'Hyphomicrobium']

# Tamaño del gráfico
plt.figure(figsize=(10, 10))

# Crear gráfico de barras horizontal
conteo_combinado.plot(kind='barh', color=["#a0d6b4", "#dda0dd"])

# Título
plt.title('Frecuencia de los géneros Hyphomicrobium y Acidibacter entre categorías')

# Etiqueta del eje Y
plt.ylabel('Categoría')

# Etiqueta del eje X
plt.xlabel('Frecuencia')

# Añadir cuadrícula
plt.grid()

# Mostrar leyenda fuera del gráfico
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# Ajustar diseño para que la leyenda no se corte
plt.tight_layout()

# Mostrar gráfico
plt.show()
<Figure size 1000x1000 with 0 Axes>
No description has been provided for this image

Conclusiones:¶

• El dataset tiene poca información que permita analizar adecuadamente los suelos considerados saludables y no saludables, debido a que no se indican esas categorías para los datos, pero para realizar el ejercicio funcionó muy bien, debido a que se logró filtrar y limpiar la información.
• Es evidente que las muestras procesadas en los tres sitios de Finlandia, presentan diferencias en la estructura del microbioma del suelo.
• El género fue el nivel taxonómico con mayor resolución para representar las diferencias entre los tres sitios, brindando un análisis más detallado.
• A pesar de que se esperaba que la estructura de los microorganismos del suelo de Costa Rica y Finlandia fuera muy diferente, por su naturaleza y método de análisis; se presentan similitudes en la importancia de taxones como Proteobacteria, Actinobacteriota y Actinomycetia.

Referencias:¶

Berihu, M., Somera, T. S., Malik, A., Medina, S., Piombo, E., Tal, O., ... & Freilich, S. (2023). A framework for the targeted recruitment of crop-beneficial soil taxa based on network analysis of metagenomics data. Microbiome, 11(1), 8.

Edwin, N. R., Fitzpatrick, A. H., Brennan, F., Abram, F., & O’Sullivan, O. (2024). An in-depth evaluation of metagenomic classifiers for soil microbiomes. Environmental Microbiome, 19(1), 19.

Hermans, S. M., Buckley, H. L., Case, B. S., Curran-Cournane, F., Taylor, M., & Lear, G. (2020). Using soil bacterial communities to predict physico-chemical variables and soil quality. Microbiome, 8, 1-13.

Instituto Nacional de Tecnología Agropecuaria. (2019). Zonificación Agroecológica para el cantón de Naranjo de Alajuela. Recuperado de http://www.platicar.go.cr/images/buscador/documents/pdf/2019/Memoria_Tecnica_NARANJO_Comp_ed.pdf \

MGnify (2020). Soil metagenome from healthy and unhealthy agricultural soil. Sampling event dataset https://doi.org/10.15468/rguc4r accessed via GBIF.org on 2024-06-04.

Plassart, P., Prévost-Bouré, N. C., Uroz, S., Dequiedt, S., Stone, D., Creamer, R., ... & Lemanceau, P. (2019). Soil parameters, land use, and geographical distance drive soil bacterial communities along a European transect. Scientific reports, 9(1), 1-17.