{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7dd5a0ca-ab7a-48cc-98c5-9b2a84219dab",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Summary statistics merger"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "id": "6c039754-6430-4aaf-90f7-46a1cac2ad30",
   "metadata": {
    "kernel": "SoS"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import yaml\n",
    "import glob\n",
    "import pandas as pd\n",
    "from Bio.Seq import Seq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "id": "632c1c04-6bc3-4ba4-b832-19c429418ad6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[0, './data/template.yml']]"
      ]
     },
     "execution_count": 146,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.read_csv('../xqtl-pipeline/pipeline/misc/data/yml_list.txt',sep = \"\\t\").values.tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 225,
   "id": "f5697bf9-5d26-43f2-9df1-76423ee4be4d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total number of sumstats:  4\n",
      "{'../xqtl-pipeline/pipeline/misc/data/testflip/snps500_flip_rea0a1.regenie.snp_stats.gz': {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}, '../xqtl-pipeline/pipeline/misc/data/testflip/snps500_rea0a1.regenie.snp_stats.gz': {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}, '../xqtl-pipeline/pipeline/misc/data/testflip/snps500.regenie.snp_stats.gz': {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}, '../xqtl-pipeline/pipeline/misc/data/testflip/snps1000.regenie.snp_stats.gz': {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}}\n",
      "../xqtl-pipeline/pipeline/misc/data/testflip/snps500_flip_rea0a1.regenie.snp_stats.gz {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}\n",
      "../xqtl-pipeline/pipeline/misc/data/testflip/snps500_rea0a1.regenie.snp_stats.gz {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}\n",
      "../xqtl-pipeline/pipeline/misc/data/testflip/snps500.regenie.snp_stats.gz {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}\n",
      "../xqtl-pipeline/pipeline/misc/data/testflip/snps1000.regenie.snp_stats.gz {'ID': 'CHR,POS', 'CHR': 'CHR', 'POS': 'POS', 'A0': 'REF', 'A1': 'ALT', 'SNP': 'SNP', 'STAT': 'BETA', 'SE': 'SE', 'P': 'P'}\n",
      "Total rows of query:  500 Total rows of subject:  500\n",
      "Overlap chr:pos 500\n",
      "              sign_flip  strand_flip  exact_match\n",
      "1:18744859         True         True        False\n",
      "1:19112524         True         True        False\n",
      "1:19112744         True         True        False\n",
      "1:19220870         True         True        False\n",
      "1:19324561         True         True        False\n",
      "...                 ...          ...          ...\n",
      "10:91959301        True         True        False\n",
      "10:93074445        True         True        False\n",
      "10:94171346        True         True        False\n",
      "10:100293231       True         True        False\n",
      "10:101803994       True         True        False\n",
      "\n",
      "[418 rows x 3 columns]\n",
      "Overlap SNPs 418\n",
      "Total rows of query:  500 Total rows of subject:  500\n",
      "Overlap chr:pos 500\n",
      "              sign_flip  strand_flip  exact_match\n",
      "1:18744859         True        False        False\n",
      "1:19112524         True        False        False\n",
      "1:19112744         True        False        False\n",
      "1:19220870         True        False        False\n",
      "1:19324561         True        False        False\n",
      "...                 ...          ...          ...\n",
      "10:91959301        True        False        False\n",
      "10:93074445        True        False        False\n",
      "10:94171346        True        False        False\n",
      "10:100293231       True        False        False\n",
      "10:101803994       True        False        False\n",
      "\n",
      "[418 rows x 3 columns]\n",
      "Overlap SNPs 418\n",
      "Total rows of query:  500 Total rows of subject:  500\n",
      "Overlap chr:pos 500\n",
      "              sign_flip  strand_flip  exact_match\n",
      "1:18744859        False        False         True\n",
      "1:19112524        False        False         True\n",
      "1:19112744        False        False         True\n",
      "1:19220870        False        False         True\n",
      "1:19324561        False        False         True\n",
      "...                 ...          ...          ...\n",
      "10:91959301       False        False         True\n",
      "10:93074445       False        False         True\n",
      "10:94171346       False        False         True\n",
      "10:100293231      False        False         True\n",
      "10:101803994      False        False         True\n",
      "\n",
      "[418 rows x 3 columns]\n",
      "Overlap SNPs 418\n",
      "Total rows of query:  1000 Total rows of subject:  500\n",
      "Overlap chr:pos 500\n",
      "              sign_flip  strand_flip  exact_match\n",
      "1:18744859        False        False         True\n",
      "1:19112524        False        False         True\n",
      "1:19112744        False        False         True\n",
      "1:19220870        False        False         True\n",
      "1:19324561        False        False         True\n",
      "...                 ...          ...          ...\n",
      "10:91959301       False        False         True\n",
      "10:93074445       False        False         True\n",
      "10:94171346       False        False         True\n",
      "10:100293231      False        False         True\n",
      "10:101803994      False        False         True\n",
      "\n",
      "[418 rows x 3 columns]\n",
      "Overlap SNPs 418\n",
      "Total number of common SNPs:  418\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/1988675.1.plot.q/ipykernel_5955/2259156742.py:25: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  new_query.STAT[pm.sign_flip] = -new_query.STAT[pm.sign_flip]\n",
      "/tmp/1988675.1.plot.q/ipykernel_5955/2259156742.py:25: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  new_query.STAT[pm.sign_flip] = -new_query.STAT[pm.sign_flip]\n",
      "/tmp/1988675.1.plot.q/ipykernel_5955/2259156742.py:25: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  new_query.STAT[pm.sign_flip] = -new_query.STAT[pm.sign_flip]\n",
      "/tmp/1988675.1.plot.q/ipykernel_5955/2259156742.py:25: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  new_query.STAT[pm.sign_flip] = -new_query.STAT[pm.sign_flip]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "All are done!!!\n"
     ]
    }
   ],
   "source": [
    "merge_sumstats('../xqtl-pipeline/pipeline/misc/data/template.yml',keep_ambiguous=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 202,
   "id": "f0e5d441-4470-41b9-b232-fbc824daeeaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "yml = load_yaml('../xqtl-pipeline/pipeline/misc/data/template.yml')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 203,
   "id": "3e5f7296-4bc6-4b17-bc93-9673535741fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_dict = parse_input(yml['INPUT'])\n",
    "target_dict = parse_input(yml['TARGET'])\n",
    "output_path = yml['OUTPUT']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 204,
   "id": "1755ac95-5210-42a9-9255-0cf00a5485c6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'../xqtl-pipeline/pipeline/misc/data/testflip/snps500.regenie.snp_stats.gz': {'ID': 'CHR,POS',\n",
       "   'CHR': 'CHR',\n",
       "   'POS': 'POS',\n",
       "   'A0': 'REF',\n",
       "   'A1': 'ALT',\n",
       "   'SNP': 'SNP',\n",
       "   'STAT': 'BETA',\n",
       "   'SE': 'SE',\n",
       "   'P': 'P'}}]"
      ]
     },
     "execution_count": 204,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "yml['TARGET']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 205,
   "id": "1052ee00-b6a8-4164-9720-ba5a01ffaace",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'../xqtl-pipeline/pipeline/misc/data/testflip/snps500_flip_rea0a1.regenie.snp_stats.gz': {'ID': 'CHR,POS',\n",
       "  'CHR': 'CHR',\n",
       "  'POS': 'POS',\n",
       "  'A0': 'REF',\n",
       "  'A1': 'ALT',\n",
       "  'SNP': 'SNP',\n",
       "  'STAT': 'BETA',\n",
       "  'SE': 'SE',\n",
       "  'P': 'P'},\n",
       " '../xqtl-pipeline/pipeline/misc/data/testflip/snps500_rea0a1.regenie.snp_stats.gz': {'ID': 'CHR,POS',\n",
       "  'CHR': 'CHR',\n",
       "  'POS': 'POS',\n",
       "  'A0': 'REF',\n",
       "  'A1': 'ALT',\n",
       "  'SNP': 'SNP',\n",
       "  'STAT': 'BETA',\n",
       "  'SE': 'SE',\n",
       "  'P': 'P'},\n",
       " '../xqtl-pipeline/pipeline/misc/data/testflip/snps500.regenie.snp_stats.gz': {'ID': 'CHR,POS',\n",
       "  'CHR': 'CHR',\n",
       "  'POS': 'POS',\n",
       "  'A0': 'REF',\n",
       "  'A1': 'ALT',\n",
       "  'SNP': 'SNP',\n",
       "  'STAT': 'BETA',\n",
       "  'SE': 'SE',\n",
       "  'P': 'P'},\n",
       " '../xqtl-pipeline/pipeline/misc/data/testflip/snps1000.regenie.snp_stats.gz': {'ID': 'CHR,POS',\n",
       "  'CHR': 'CHR',\n",
       "  'POS': 'POS',\n",
       "  'A0': 'REF',\n",
       "  'A1': 'ALT',\n",
       "  'SNP': 'SNP',\n",
       "  'STAT': 'BETA',\n",
       "  'SE': 'SE',\n",
       "  'P': 'P'},\n",
       " '../xqtl-pipeline/pipeline/misc/data/testflip/flip/snps500_flip.regenie.snp_stats.gz': None}"
      ]
     },
     "execution_count": 205,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "input_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "id": "7375ed33-8a1f-46c5-b5b8-a0f959167a07",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 197,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "target_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "id": "bf22400f-e5b2-452d-aa85-0ae1d56f67f0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 160,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(target_dict.values())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "id": "f777ccf9-9084-430a-bb2a-50367d90c0cc",
   "metadata": {
    "kernel": "SoS"
   },
   "outputs": [],
   "source": [
    "def merge_sumstats(yml,keep_ambiguous):\n",
    "    #parse yaml\n",
    "    yml = load_yaml(yml)\n",
    "    input_dict = parse_input(yml['INPUT'])\n",
    "    target_dict = parse_input(yml['TARGET'])\n",
    "    output_path = yml['OUTPUT']\n",
    "\n",
    "    input_dict[list(target_dict.keys())[0]] = list(target_dict.values())[0]\n",
    "    lst_sumstats_file = [os.path.basename(i) for i in input_dict.keys()]\n",
    "    print('Total number of sumstats: ',len(lst_sumstats_file))\n",
    "    if len(set(lst_sumstats_file))<len(lst_sumstats_file):\n",
    "        raise Exception(\"There are duplicated names in \", lst_sumstats_file)\n",
    "    #read all sumstats\n",
    "    print(input_dict)\n",
    "    lst_sumstats = {os.path.basename(i):read_sumstat(i,j) for i,j in input_dict.items()}\n",
    "    nqs = []\n",
    "    for query in lst_sumstats.values():\n",
    "        nq,_ = snps_match(query,lst_sumstats[os.path.basename(list(target_dict.keys())[0])],keep_ambiguous)\n",
    "        nqs.append(nq)\n",
    "    #get common snps\n",
    "    common_snps = set.intersection(*[set(nq.SNP) for nq in nqs])\n",
    "    print('Total number of common SNPs: ',len(common_snps))\n",
    "    #write out new smustats\n",
    "    for output_sumstats,nq in zip(lst_sumstats_file,nqs):\n",
    "        sumstats = nq[nq.SNP.isin(common_snps)]\n",
    "        sumstats.to_csv(os.path.join(output_path, output_sumstats), sep = \"\\t\", header = True, index = False,compression='gzip')\n",
    "    print('All are done!!!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "178c52d6-c861-4172-a0d7-7aa458d19a75",
   "metadata": {
    "kernel": "SoS"
   },
   "outputs": [],
   "source": [
    "def load_yaml(yaml_file):\n",
    "    with open(yaml_file, \"r\") as stream:\n",
    "        try:\n",
    "            yml = yaml.safe_load(stream)\n",
    "        except yaml.YAMLError as exc:\n",
    "            print(exc)\n",
    "    return yml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 222,
   "id": "b1d86f85-1a1c-419f-85af-65a37708b431",
   "metadata": {
    "kernel": "SoS"
   },
   "outputs": [],
   "source": [
    "def parse_input(yml_input):\n",
    "    input_dict = {}\n",
    "    for i in yml_input:\n",
    "        for name in glob.glob(list(i.keys())[0]):\n",
    "            input_dict[name] = list(i.values())[0].copy()\n",
    "    return input_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 213,
   "id": "683415f0-3c4c-4425-b6e5-adb71d755b21",
   "metadata": {},
   "outputs": [],
   "source": [
    "a_dict = {\"a\": 1, \"B\": 2, \"C\": 3}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 215,
   "id": "816350a0-cd12-44a1-ac0d-cdb42915ebc8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 215,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a_dict.pop('a')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 216,
   "id": "cbc43f1b-39d4-4b66-9e3c-f8602b50fd2a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'B': 2, 'C': 3}"
      ]
     },
     "execution_count": 216,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 212,
   "id": "35ff7992-fa9d-43af-a348-2233a6e792e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['a', 'b']"
      ]
     },
     "execution_count": 212,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'a,b'.split(',')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 224,
   "id": "53760ce1-b374-4ffd-baa5-46dc00090541",
   "metadata": {},
   "outputs": [],
   "source": [
    "def read_sumstat(file, config=None):\n",
    "    print(file,config)\n",
    "    try:\n",
    "        sumstats = pd.read_csv(file, compression='gzip', header=0, sep='\\t', quotechar='\"')\n",
    "    except:\n",
    "        sumstats = pd.read_csv(file, header=0, sep='\\t', quotechar='\"')\n",
    "    if config is not None:\n",
    "        try:\n",
    "            sumstats.index = sumstats.loc[:,config.pop('ID').split(',')].astype(str).agg(':'.join, axis=1)\n",
    "            sumstats = sumstats.loc[:,list(config.values())]\n",
    "        except:\n",
    "            raise ValueError(f'According to config_file, input summary statistics should have the following columns: %s' % list(config.values()))\n",
    "        sumstats.columns = list(config.keys())\n",
    "    sumstats.SNP = 'chr'+sumstats.CHR.astype(str) + ':' + sumstats.POS.astype(str) + ':' + sumstats.A0.astype(str) + ':' + sumstats.A1.astype(str)\n",
    "    sumstats.CHR = sumstats.CHR.astype(int)\n",
    "    sumstats.POS = sumstats.POS.astype(int)\n",
    "    return sumstats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 209,
   "id": "521500a7-b775-4f5d-89b1-ede9253c5c58",
   "metadata": {},
   "outputs": [],
   "source": [
    "['a','b'].remove('a')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 191,
   "id": "1a5d768d-c91e-4614-ad18-9a8f59712b1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def snps_match(query,subject,keep_ambiguous=True):\n",
    "    query.index = query.iloc[:,:2].astype(str).agg(':'.join, axis=1)\n",
    "    subject.index = subject.iloc[:,:2].astype(str).agg(':'.join, axis=1)\n",
    "    #overlap snps by chr+pos\n",
    "    print(\"Total rows of query: \",query.shape[0],\"Total rows of subject: \",subject.shape[0])\n",
    "    subject = subject[subject.index.isin(query.index)]\n",
    "    query = query.loc[subject.index]\n",
    "    print(\"Overlap chr:pos\",query.shape[0])\n",
    "    if query.index.duplicated().any():\n",
    "        raise Exception(\"There are duplicated chr:pos\")\n",
    "    pm = pair_match(query.A1,query.A0,subject.A1,subject.A0)\n",
    "    if keep_ambiguous:\n",
    "        print('Warning: there are',sum(~pm.ambiguous),'ambiguous SNPs')\n",
    "        pm = pm.iloc[:,1:]\n",
    "    else:\n",
    "        pm = pm[~pm.ambiguous].iloc[:,1:]\n",
    "        print(pm)\n",
    "    keep_idx = pm.any(axis=1)\n",
    "    keep_idx = keep_idx.index[keep_idx==True]\n",
    "    print(\"Overlap SNPs\",len(keep_idx))\n",
    "    #overlap snps by chr+pos+alleles.\n",
    "    new_subject = subject.loc[keep_idx]\n",
    "    #update beta and snp info\n",
    "    new_query = pd.concat([new_subject.iloc[:,:5],query.loc[keep_idx].iloc[:,5:]],axis=1)\n",
    "    new_query.STAT[pm.sign_flip] = -new_query.STAT[pm.sign_flip]\n",
    "    return new_query,new_subject"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "729d47bd-f3e0-4b1e-8171-cd4d78a059bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def pair_match(a1,a2,ref1,ref2):\n",
    "    # a1 and a2 are the first data-set\n",
    "\t# ref1 and ref2 are the 2nd data-set\n",
    "\t# Make all the alleles into upper-case, as A,T,C,G:\n",
    "    a1 = a1.str.upper()\n",
    "    a2 = a2.str.upper()\n",
    "    ref1 = ref1.str.upper()\n",
    "    ref2 = ref2.str.upper()\n",
    "\t# Strand flip, to change the allele representation in the 2nd data-set\n",
    "    flip1 = ref1.apply(strand_flip)\n",
    "    flip2 = ref2.apply(strand_flip)\n",
    "    result = {}\n",
    "    result[\"ambiguous\"] = ((a1==\"A\") & (a2==\"T\")) | ((a1==\"T\") & (a2==\"A\")) | ((a1==\"C\") & (a2==\"G\")) | ((a1==\"G\") & (a2==\"C\"))\n",
    "    # as long as scenario 1 is involved, sign_flip will return TRUE\n",
    "    result[\"sign_flip\"] = ((a1==ref2) & (a2==ref1)) | ((a1==flip2) & (a2==flip1))\n",
    "\t# as long as scenario 2 is involved, strand_flip will return TRUE\n",
    "    result[\"strand_flip\"] = ((a1==flip1) & (a2==flip2)) | ((a1==flip2) & (a2==flip1))\n",
    "\t# remove other cases, eg, tri-allelic, one dataset is A C, the other is A G, for example.\n",
    "    result[\"exact_match\"] = ((a1 == ref1) & (a2 == ref2))\n",
    "    return pd.DataFrame(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "3d2c2e38-9e14-458e-93df-8ba2a5fcc8fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "def strand_flip(s):\n",
    "    return ''.join(Seq(s).reverse_complement())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e6b63d8-17e9-456b-b4fb-f453f348f76c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
