{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Data imputation on a Real Public Dataset, Herold et al. (2020)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The article “Integration of time-series meta-omics data reveals how microbial ecosystems respond to disturbance” explores the dynamic nature of microbial communities in biological wastewater treatment plants (BWWTPs) by combining metagenomics (MG), metatranscriptomics (MT), and other meta-omics approaches. The study provides a detailed analysis of these communities’ resilience and resistance to environmental disturbances. We are going to use the data from this study to perform data imputation and analysis. Specifically, we will focus on the following figures:\n", "\n", "#### Figure 3 - Community Structure and Function Dynamics:\n", "Figure 3 presents the community structure and function dynamics over time, focusing on the relative abundance and expression levels of microbial populations. The data are derived from recovered populations represented by reconstructed metagenome-assembled genomes (rMAGs), which show genetic composition and activity through MG and MT depth coverage, with mapping percentages of 26% ± 3% and 27% ± 3%, respectively. The relative abundance data are categorized by genus-level taxonomic assignments, and less abundant genera are grouped under “Other” if their mean abundance is below 2%.\n", "\n", "This figure also includes ordination plots based on Bray–Curtis dissimilarity of rMAGs’ relative abundances, constrained by abiotic factors such as metabolite levels, metabolite ratios, and physicochemical parameters (shown as black arrows). The results highlight seasonal shifts and environmental influences on microbial community dynamics, with time-course sampling illustrated through connecting points and color coding by the month of sampling.\n", "\n", "#### Figure 4 - Metabolite Levels and Environmental Dynamics:\n", "Figure 4 presents time-series data on metabolite levels and physicochemical parameters, highlighting fluctuations in metabolite classes, types, and measurement subtypes. It uses a heatmap to display Z-score transformed intensities of metabolites and other parameters, showcasing intracellular and extracellular metabolite levels, ratios, and several environmental parameters collected manually or through continuous monitoring at a wastewater treatment plant. This data reflects how environmental and metabolite dynamics interact, impacting microbial activity and metabolism over time (Herold et al., 2020).\n", "\n", "By focusing on these figures, our VAR (Vector Autoregression) inference and Gaussian Process imputer (GP Imputer) can leverage time-series trends and correlations between microbial populations and their environment within BWWTP systems.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From c:\\ProgramData\\anaconda3\\envs\\MIMIC\\Lib\\site-packages\\tf_keras\\src\\losses.py:2976: The name tf.losses.sparse_softmax_cross_entropy is deprecated. Please use tf.compat.v1.losses.sparse_softmax_cross_entropy instead.\n", "\n", "WARNING:tensorflow:From c:\\ProgramData\\anaconda3\\envs\\MIMIC\\Lib\\site-packages\\tensorflow_probability\\python\\internal\\backend\\numpy\\_utils.py:48: The name tf.logging.TaskLevelStatusMessage is deprecated. Please use tf.compat.v1.logging.TaskLevelStatusMessage instead.\n", "\n", "WARNING:tensorflow:From c:\\ProgramData\\anaconda3\\envs\\MIMIC\\Lib\\site-packages\\tensorflow_probability\\python\\internal\\backend\\numpy\\_utils.py:48: The name tf.control_flow_v2_enabled is deprecated. Please use tf.compat.v1.control_flow_v2_enabled instead.\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING (pytensor.tensor.blas): Using NumPy C-API based implementation for BLAS functions.\n" ] } ], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "\n", "import pandas as pd\n", "import seaborn as sns\n", "\n", "\n", "from mimic.data_imputation.impute_GP import GPImputer\n", "from mimic.model_infer.infer_VAR_bayes import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imputing Microbial Community Data (Figure 3) using Gaussian Process Imputer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = pd.read_csv(r'Source Data/fig3ab.tsv', delimiter='\\t')\n", "\n", "data.head(15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Separate the data into MG and MT datasets\n", "\n", "The data is separated into two datasets, MG and MT. The MG dataset contains the meta-genomics data, while the MT dataset contains the meta-transcriptomics. We will be using the MG dataset in this example.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# separate the data into MG and MT datasets\n", "\n", "dataMG = data[data['type'] == 'MG']\n", "\n", "dataMG.info()\n", "\n", "dataMT = data[data['type'] == 'MT']\n", "\n", "dataMT.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will proceed with some data wrangling to prepare the data for the imputation process.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Data cleaning\n", "# Rename Nan in the genus column to 'Other'\n", "\n", "dataMG['genus'] = dataMG['genus'].replace(np.nan, 'Other')\n", "\n", "# Drop the 'type' column\n", "dataMG = dataMG.drop(columns=['type'])\n", "\n", "# Change relative Date columns to days\n", "dataMG['date'] = pd.to_datetime(dataMG['date'])\n", "dataMG['date'] = (dataMG['date'] - dataMG['date'].min()).dt.days\n", "\n", "# Change the 'genus' column to a categorical column\n", "dataMG['genus'] = dataMG['genus'].astype('category')\n", "\n", "\n", "dataMG.info()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Plot the data, relative abundance (value) vs. days (date) for each genus\n", "\n", "\n", "sns.set(style=\"whitegrid\")\n", "plt.figure(figsize=(12, 6))\n", "sns.lineplot(x='date', y='val', hue='genus', data=dataMG)\n", "plt.title('Microbial Growth Data')\n", "plt.xlabel('Days')\n", "plt.ylabel('Relative Abundance')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Pivot the data so each 'genus' is a column\n", "dataMG_pivot = dataMG.pivot(\n", " index='date', columns='genus', values='val').fillna(0)\n", "\n", "# Create a stacked area chart\n", "plt.figure(figsize=(12, 6))\n", "plt.stackplot(dataMG_pivot.index, dataMG_pivot.T, labels=dataMG_pivot.columns)\n", "\n", "plt.title('Microbial Growth Data')\n", "plt.xlabel('Days')\n", "plt.ylabel('Relative Abundance')\n", "plt.legend(loc='upper left')\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Do the same data cleaning for the MT data set\n", "\n", "# Rename Nan in the genus column to 'Other'\n", "\n", "dataMT['genus'] = dataMT['genus'].replace(np.nan, 'Other')\n", "\n", "# Drop the 'type' column\n", "dataMT = dataMT.drop(columns=['type'])\n", "\n", "# Change relative Date columns to days\n", "dataMT['date'] = pd.to_datetime(dataMT['date'])\n", "dataMT['date'] = (dataMT['date'] - dataMT['date'].min()).dt.days\n", "\n", "# Change the 'genus' column to a categorical column\n", "dataMT['genus'] = dataMT['genus'].astype('category')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Plot the data, relative abundance (value) vs. days (date) for each genus\n", "\n", "\n", "sns.set(style=\"whitegrid\")\n", "plt.figure(figsize=(12, 6))\n", "sns.lineplot(x='date', y='val', hue='genus', data=dataMT)\n", "plt.title('Microbial Meta Transcriptomics Data')\n", "plt.xlabel('Days')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Pivot the data so each 'genus' is a column\n", "dataMT_pivot = dataMT.pivot(\n", " index='date', columns='genus', values='val').fillna(0)\n", "\n", "# Pivot the MG data so each 'genus' is a column\n", "dataMG_pivot = dataMG.pivot(\n", " index='date', columns='genus', values='val').fillna(0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "# Create a DataFrame with a complete range of days\n", "all_days = pd.DataFrame(\n", " {'days': range(dataMG['date'].min(), dataMG['date'].max() + 1)})\n", "\n", "# Reindex the pivoted DataFrame to include all days, filling missing values with None\n", "complete_dataMG = dataMG_pivot.reindex(all_days['days'])\n", "\n", "complete_dataMG.head(15)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "complete_dataMG.columns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we pick one column, say 'Acinetobacter', we can see that there are missing values in the dataset. We will use the GP imputator to fill in the missing values." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "complete_dataMG['Acinetobacter'].isnull().sum()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Reset the index\n", "complete_dataMG_reset = complete_dataMG.reset_index()\n", "\n", "# Make index as float\n", "complete_dataMG_reset['days'] = complete_dataMG_reset['days'].astype(float)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Now 'days' is a regular column and you can use it as a feature\n", "new_dataset = GPImputer().impute_missing_values(\n", " complete_dataMG_reset, ['days'], ['Acidimicrobium',\n", " 'Acinetobacter', 'Albidiferax'], 'Acinetobacter'\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "new_dataset['Acinetobacter'].isnull().sum()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see now that the data imputator calculated the missing values for the 'Acinetobacter' column. However, as we can see fro the graph, the imputed values are not great. This can be caused by multiple factors, so we are going to explore different methods to better impute the missing values. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are first going to try to do imputation using VGP imputation. This is a variational Gaussian process imputation method that is more robust to missing data and uses multiple imputs to calculate the missing values. However, as we will see, this method creates an imputation that is heavily biased by other imputs, so it doesn't work in our case to determine the missing values of the 'Acinetobacter' column." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Now 'days' is a regular column and you can use it as a feature\n", "new_dataset = GPImputer().impute_missing_values(\n", " complete_dataMG_reset, ['days'], ['Acidimicrobium', 'Acinetobacter', 'Albidiferax',\n", " 'Candidatus Microthrix', 'Chitinophaga pinensis',\n", " 'Dechloromonas', 'Haliscomenobacter', 'Intrasporangium',\n", " 'Leptospira', 'Other', 'Xanthomonas', 'mean abundance < 2%'], 'Acinetobacter'\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we, isntead, do a single imputation using only a GPR model with a single imput ('Acinetobacter'), we can see that the imputation is much better. This is because the imputation is not biased by other imputs, and the model can better predict the missing values." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Now 'days' is a regular column and you can use it as a feature\n", "new_dataset2 = GPImputer().impute_missing_values(\n", " complete_dataMG_reset, ['days'], ['Acinetobacter'], 'Acinetobacter'\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Save the dataset (new_dataset2) to a csv file\n", "new_dataset2.to_csv(r'imputed_data.csv', index=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we will proceed to do a single imput GPR model for all the columns in the dataset. We will then use the VAR inference method to infer the causal relationships between the variables in the time series." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# do the imputation for each column (genus) in the dataset\n", "# Copy the original dataset\n", "new_dataset_complete = complete_dataMG_reset.copy()\n", "\n", "for genus in complete_dataMG.columns:\n", " # Impute the missing values for the current column\n", " imputed_data = GPImputer().impute_missing_values(\n", " complete_dataMG_reset, ['days'], [genus], genus\n", " )\n", "\n", " # Update the column in the new dataset with the imputed data\n", " new_dataset_complete[genus] = imputed_data[genus]\n", "\n", "# Save the dataset (new_dataset_complete) to a csv file\n", "new_dataset_complete.to_csv('imputed_data.csv', index=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imputing Metabolite Data (Figure 4) using Gaussian Process Imputer" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "metabolites = pd.read_csv(r'Source Data/fig4.tsv', delimiter='\\t')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This dataset contains several columns, each representing different attributes related to microbial activity and metabolite concentrations. Here is a detailed explanation of each column:\n", "\n", "- **date**: The date when the sample was collected.\n", "- **L1**: The functional category or pathway associated with the data point.\n", "- **mp_tot**: Total metaproteomic count for that date and pathway.\n", "- **mt_tot**: Total metatranscriptomic count for that date and pathway.\n", "- **L1sum_MP**: Sum of metaproteomic counts for that specific functional category (L1).\n", "- **L1sum_MT**: Sum of metatranscriptomic counts for that specific functional category (L1).\n", "- **L1rel_MP**: Relative abundance of metaproteomic counts within the functional category.\n", "- **L1rel_MT**: Relative abundance of metatranscriptomic counts within the functional category.\n", "- **MP_std_L1**: Standard deviation of metaproteomic counts for that functional category.\n", "- **MT_std_L1**: Standard deviation of metatranscriptomic counts for that functional category.\n", "- **cor**: Correlation value between metaproteomic and metatranscriptomic data for that pathway.\n", "- **newL1**: A reformatted version of the functional category name, often including a correlation coefficient (r value) for easier readability in the figure.\n", "\n", "We will be using the `L1rel_MP` and `L1rel_MT` columns in this example to use it with the VAR inference method to infer the causal relationships between the variables in the time series with the meta transcriptomics (MT) and meta genomics (MG) data." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | date | \n", "L1 | \n", "L1rel_MP | \n", "
---|---|---|---|
0 | \n", "2011-03-21 | \n", "01_Fermentation | \n", "0.096899 | \n", "
1 | \n", "2011-03-21 | \n", "02_Homoacetogenesis | \n", "0.009890 | \n", "
2 | \n", "2011-03-21 | \n", "03_Superpathway of thiosulfate metabolism (Des... | \n", "0.000579 | \n", "
3 | \n", "2011-03-21 | \n", "04_Utililization of sugar, conversion of pento... | \n", "0.000535 | \n", "
4 | \n", "2011-03-21 | \n", "05_Fatty acid oxidation | \n", "0.025483 | \n", "
5 | \n", "2011-03-21 | \n", "06_Amino acid utilization biosynthesis metabolism | \n", "0.122784 | \n", "
6 | \n", "2011-03-21 | \n", "07_Nucleic acid metabolism | \n", "0.119531 | \n", "
7 | \n", "2011-03-21 | \n", "08_Hydrocarbon degradation | \n", "0.005524 | \n", "
8 | \n", "2011-03-21 | \n", "09_Carbohydrate Active enzyme - CAZy | \n", "0.003921 | \n", "
9 | \n", "2011-03-21 | \n", "10_TCA cycle | \n", "0.045398 | \n", "
10 | \n", "2011-03-21 | \n", "11_Nitrogen cycle | \n", "0.026063 | \n", "
11 | \n", "2011-03-21 | \n", "12_Transporters | \n", "0.015593 | \n", "
12 | \n", "2011-03-21 | \n", "13_Hydrogen metabolism | \n", "0.000846 | \n", "
13 | \n", "2011-03-21 | \n", "14_Methanogenesis | \n", "0.003520 | \n", "
14 | \n", "2011-03-21 | \n", "15_Methylotrophy | \n", "0.009267 | \n", "
L1 | \n", "01_Fermentation | \n", "02_Homoacetogenesis | \n", "03_Superpathway of thiosulfate metabolism (Desulfovibrio sulfodismutans) | \n", "04_Utililization of sugar, conversion of pentose to EMP pathway intermediates | \n", "05_Fatty acid oxidation | \n", "06_Amino acid utilization biosynthesis metabolism | \n", "07_Nucleic acid metabolism | \n", "08_Hydrocarbon degradation | \n", "09_Carbohydrate Active enzyme - CAZy | \n", "10_TCA cycle | \n", "... | \n", "12_Transporters | \n", "13_Hydrogen metabolism | \n", "14_Methanogenesis | \n", "15_Methylotrophy | \n", "16_Embden Meyerhof-Parnos (EMP) | \n", "17_Gluconeogenesis | \n", "18_Sulfur compounds metabolism | \n", "19_Saccharide and derivated synthesis | \n", "20_Hydrolysis of polymers | \n", "21_Cellular response to stress | \n", "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
date | \n", "\n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " |
0 | \n", "0.096899 | \n", "0.009890 | \n", "0.000579 | \n", "0.000535 | \n", "0.025483 | \n", "0.122784 | \n", "0.119531 | \n", "0.005524 | \n", "0.003921 | \n", "0.045398 | \n", "... | \n", "0.015593 | \n", "0.000846 | \n", "0.003520 | \n", "0.009267 | \n", "0.030473 | \n", "0.016261 | \n", "0.005346 | \n", "0.009267 | \n", "0.006193 | \n", "0.037334 | \n", "
1 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
2 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
3 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
4 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
5 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
6 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
7 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
8 | \n", "0.133734 | \n", "0.011645 | \n", "0.000340 | \n", "0.000870 | \n", "0.033575 | \n", "0.129235 | \n", "0.108779 | \n", "0.004688 | \n", "0.003063 | \n", "0.052367 | \n", "... | \n", "0.018224 | \n", "0.001059 | \n", "0.004159 | \n", "0.016334 | \n", "0.035428 | \n", "0.018678 | \n", "0.003592 | \n", "0.009150 | \n", "0.006692 | \n", "0.042688 | \n", "
9 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
10 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
11 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
12 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
13 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
14 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "... | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "
15 rows × 21 columns
\n", "