Science Score: 18.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (4.4%) to scientific vocabulary
Last synced: 10 months ago · JSON representation ·

Repository

Basic Info
  • Host: GitHub
  • Owner: ComplexInfo
  • Language: Jupyter Notebook
  • Default Branch: main
  • Size: 5.85 MB
Statistics
  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Created over 3 years ago · Last pushed over 3 years ago
Metadata Files
Readme Citation

README.md

Documentation

This repository contains the codes for implementing the algorithm proposed in the paper titled "Estimating Exposure to Information on Social Networks"

Directions for numerical simulations

To reproduce the simulation results of the paper, run the Jupyter notebook titled "Code_Exposure".

Outputs: The empirical results will be stored in the folders "NumericalResults" and "EmpiricalResults" and the final figures are stored in folder named "Figures".

Directions for empirical simulations

To reproduce the empirical results of the paper, use the Jupyter notebook titled "Citation Network Simulation". The full dataset used for empirical simulations is available at: https://lfs.aminer.cn/lab-datasets/citation/citation-network1.zip

Owner

  • Name: Buddhika Nettasinghe
  • Login: ComplexInfo
  • Kind: user

Citation (Citation Network Simulation.ipynb)

{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3a4175d7",
   "metadata": {},
   "source": [
    "# Citation Network Simulation\n",
    "\n",
    "This program is broken into three parts:\n",
    "1. Creating the graph/network\n",
    "2. Determining phrases within the the network,\n",
    "3. Identifying sharing/exposed nodes and performing the estimate. \n",
    "\n",
    "The results of all calculations are written to a backup folder; use the get functions (e.g. getDict or getFile) to import calculations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c831674b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#Imports\n",
    "import collections\n",
    "import nltk\n",
    "from nltk.collocations import *\n",
    "import math\n",
    "import os\n",
    "import json\n",
    "import numpy as np\n",
    "import csv\n",
    "import matplotlib\n",
    "# matplotlib.use('TkAgg')\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "from scipy.stats import bernoulli\n",
    "from scipy.stats import pareto\n",
    "import networkx as nx\n",
    "import random\n",
    "import scipy.io\n",
    "import collections\n",
    "import pickle\n",
    "from mpl_toolkits.axes_grid1.inset_locator import (inset_axes,InsetPosition,mark_inset)\n",
    "import copy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c42977cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: nonblankLines\n",
    "Inputs: A line\n",
    "Purpose: Handles files with blank lines\n",
    "\"\"\"\n",
    "def nonblankLines(f):\n",
    "    for l in f:\n",
    "        line = l.rstrip()\n",
    "        line = line.replace('\\n','')\n",
    "        if line:\n",
    "            yield line\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "28192131",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: listToFile\n",
    "Input: A list and a file to write to\n",
    "Purpuse: Writes the list in a file\n",
    "\"\"\"\n",
    "def listToFile(items,writefile):\n",
    "    with open(writefile,'w',encoding=\"utf8\") as output:\n",
    "        for item in items:\n",
    "            output.write(\"%s\\n\" % item)\n",
    "    output.close()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "09f06798",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: getDict\n",
    "Inputs: A file to read from\n",
    "Purpose: Returns a dictionary\n",
    "\"\"\"\n",
    "def getDict(file):\n",
    "    #Reading the data from a file\n",
    "    with open(file,encoding=\"utf8\") as f:\n",
    "        data = f.read()\n",
    "    f.close()\n",
    "\n",
    "    return json.loads(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "6c6c6ffa",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: getList\n",
    "Inputs: A file to read from\n",
    "Purpose: Returns a list\n",
    "\"\"\"\n",
    "def getList(readfile):\n",
    "    item = []\n",
    "    with open(readfile, 'r', encoding=\"utf8\") as input:\n",
    "        for lines in input:\n",
    "            item.append(lines)\n",
    "    input.close()\n",
    "    return item"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "7071c80e",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: search\n",
    "Inputs: list and dictionary\n",
    "Purpose: To search a list of key and determine\n",
    "\"\"\"\n",
    "def search(listOfItems, dict):\n",
    "    for i in range(len(listOfItems)):  # search to see if friend shared\n",
    "        #print(dict[str(listOfItems[i])])\n",
    "        if dict[int(listOfItems[i])] == 1:\n",
    "            return True\n",
    "    return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "37452203",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: validKey\n",
    "Inputs: A dictionary and an item\n",
    "Purpose: Determines if item is a key\n",
    "         in the dictionary\n",
    "\"\"\"\n",
    "def validKey(dict, key):\n",
    "    if key in dict.keys():\n",
    "        return True\n",
    "    else:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "bbe11d47",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: truncate\n",
    "Inputs: A float and number\n",
    "Purpose: Truncates/pads a float f to n decimal places without rounding\n",
    "\"\"\"\n",
    "def truncate(f, n):\n",
    "    s = '{}'.format(f)\n",
    "    if 'e' in s or 'E' in s:\n",
    "        return '{0:.{1}f}'.format(f, n)\n",
    "    i, p, d = s.partition('.')\n",
    "    return '.'.join([i, (d+'0'*n)[:n]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "773c1d32",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: createHashtable\n",
    "Inputs: nodes\n",
    "Purpose: Creates a dictionary with the nodes as the keys and the index as the value\n",
    "\"\"\"\n",
    "def createHashtable(nodes):\n",
    "    hashtable = {}\n",
    "    for i in range(len(nodes)):\n",
    "        hashtable[nodes[i]] = i\n",
    "    return hashtable"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a74aea85",
   "metadata": {},
   "source": [
    "## Part I- Creating the graph/network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "dbf2e948",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: parseFile\n",
    "Inputs: A file to read from and a file to write to\n",
    "Purpose: Writes the data in a more legible format\n",
    "\"\"\"\n",
    "def parseFile(readfile, writefile=\"revisedACM.txt\"):\n",
    "    #Opening & iterating through the file\n",
    "    with open(readfile, 'r', encoding=\"utf8\") as infile, \\\n",
    "            open('backupFiles/{}'.format(writefile), 'w', encoding=\"utf8\") as outfile:\n",
    "\n",
    "        #Cleaning up the data\n",
    "        for line in infile:\n",
    "            line = line.replace(\"#\", \"\")\n",
    "            line = line.replace(\"index\", \"ID: \",1)\n",
    "            line = line.replace(\"*\", \"Title: \")\n",
    "            line = line.replace(\"%\", \"Reference: \")\n",
    "\n",
    "            #Only writing the titles, IDs, and references to a new file\n",
    "            if line.startswith(\"Title: \") or line.startswith(\"ID: \") \\\n",
    "                    or line.startswith(\"Reference: \") :\n",
    "                    outfile.write(line)\n",
    "\n",
    "    #Closing the files\n",
    "    outfile.close()\n",
    "    infile.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "c7b0bb1c",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: createPapers\n",
    "Inputs: A file to read from and a file to write to\n",
    "Purpose: Creates a dictionary with a paper's ID as the key\n",
    "         and references and title as the value\n",
    "\"\"\"\n",
    "def createPapers(readfile = \"revisedACM.txt\", writefile=\"papers.txt\"):\n",
    "    #Initializing the dictionary and temporary variables\n",
    "    paper = {}\n",
    "    reference = []\n",
    "    title =\"\"\n",
    "    ID = -1\n",
    "\n",
    "    #Opening & iterating through the file\n",
    "    with open('backupFiles/{}'.format(readfile), 'r', encoding=\"utf8\") as input:\n",
    "        for lines in input:\n",
    "\n",
    "            #Determining the title\n",
    "            if lines.startswith(\"Title: \"):\n",
    "                #Adding paper to the dictionary of papers\n",
    "                paper[int(ID)] = [reference,title]\n",
    "\n",
    "                #Resetting the temporary variables\n",
    "                reference = []\n",
    "                title = \"\"\n",
    "                ID = \"\"\n",
    "\n",
    "                #Removes the word 'Title' from the line\n",
    "                title = lines.split(' ', 1)[1].lower()\n",
    "                title = title.replace('\\n','')\n",
    "\n",
    "            #Determining the ID\n",
    "            elif lines.startswith(\"ID: \"):\n",
    "                ID = int(lines.split(' ', 1)[1])\n",
    "\n",
    "            #Determining the references\n",
    "            elif lines.startswith(\"Reference: \"):\n",
    "                reference.append(int(lines.split(' ', 1)[1]))\n",
    "\n",
    "    #Deleting the temporary paper from the dictionary\n",
    "    del paper[-1]\n",
    "\n",
    "    #Writing to the file\n",
    "    with open('backupFiles/{}'.format(writefile), 'w') as output:\n",
    "        output.write(json.dumps(paper))\n",
    "    output.close()\n",
    "\n",
    "    return paper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "3c340ed9",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: createNetwork\n",
    "Inputs: A dictionary of papers and a file to write to\n",
    "Purpose: To create a dictionary with a paper and its references\n",
    "         nodes -> following\n",
    "\"\"\"\n",
    "def createNetwork(papers, writefile='network.txt'):\n",
    "    #Initiliazing the dictionary\n",
    "    networkDictionary = {}\n",
    "\n",
    "    #Iterating through the papers\n",
    "    for key, value in papers.items():\n",
    "        #Only adding papers with references to the network\n",
    "        if len(value[0]) >= 1:\n",
    "            networkDictionary[int(key)] = value[0]\n",
    "\n",
    "    #Writing to the file\n",
    "    with open('backupFiles/{}'.format(writefile), 'w') as output:\n",
    "        output.write(json.dumps(networkDictionary))\n",
    "    output.close()\n",
    "\n",
    "    return networkDictionary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "8d2b6843",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: createReverseNetwork\n",
    "Inputs: The network and a file to write to\n",
    "Purpose: To create a dictionary with a paper and others that referenced it\n",
    "         nodes -> followers\n",
    "\"\"\"\n",
    "def createReverseNetwork(net, writefile='reversedNetwork.txt'):\n",
    "    #Initiliazing the dictionary\n",
    "    reverse = {}\n",
    "\n",
    "    #Iterating through the network\n",
    "    for key,value in net.items():\n",
    "        #Switching the order from node-> following to node->followers\n",
    "        for i in range(len(value)):\n",
    "            reverse.setdefault(int(value[i]), [])\n",
    "            reverse[int(value[i])].append(int(key))\n",
    "\n",
    "    #Writing to the file\n",
    "    with open('backupFiles/{}'.format(writefile), 'w') as output:\n",
    "        output.write(json.dumps(reverse))\n",
    "    output.close()\n",
    "\n",
    "    return reverse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "55123f34",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: createEdgelist\n",
    "Inputs: The network and a file to write to\n",
    "Purpose: Writes the head and tail of a link (i.e. edge)\n",
    "\"\"\"\n",
    "def createEdgelist(network,file=\"edgeList.txt\"):\n",
    "    #Creating file with edge lists\n",
    "    fileName = open('backupFiles/{}'.format(file),\"w\",encoding=\"utf8\")\n",
    "    for key,value in network.items():\n",
    "        for j in range(len(value)):\n",
    "            writer = csv.writer(fileName,delimiter= '\\t')\n",
    "            writer.writerows(zip([key],[value[j]]))\n",
    "    fileName.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "18a38fd4",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: createGraph\n",
    "Inputs: A file to read from\n",
    "Purpose: Creates a graph using networkx\n",
    "\"\"\"\n",
    "def createGraph(file=\"edgeList.txt\"):        \n",
    "    # Generating the graph\n",
    "    G = nx.Graph()\n",
    "    edges = nx.read_edgelist('backupFiles/{}'.format(file))\n",
    "    G.add_edges_from(edges.edges)\n",
    "    return G"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "7e6c40e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: identifyNodes\n",
    "Inputs: The reversed network and a file to write to\n",
    "Purpose: Determines the total number of nodes in the network\n",
    "\"\"\"\n",
    "def identifyNodes(reverseNetwork, writefile=\"nodes.txt\"):\n",
    "    #Initiliazing the dictionary\n",
    "    Nodes = {}\n",
    "\n",
    "    #Iterating through the network\n",
    "    for key,value in reverseNetwork.items():\n",
    "        #Determining if node is already in the nodes dictionary\n",
    "        if not validKey(Nodes,int(key)):\n",
    "            #Adding nodes that have followers\n",
    "            Nodes[int(key)]= \"\"\n",
    "\n",
    "        #Iterating through nodes that are followers\n",
    "        for j in range(len(value)):\n",
    "            # Determining if node is already in the nodes dictionary\n",
    "            if not validKey(Nodes,int(value[j])):\n",
    "                #Adding nodes to the dictionary\n",
    "                Nodes[int(value[j])]= \"\"\n",
    "\n",
    "    #Writing to the file\n",
    "    with open('backupFiles/{}'.format(writefile),'w') as output:\n",
    "        output.write(json.dumps(Nodes))\n",
    "    output.close()\n",
    "\n",
    "    return Nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "707f195f",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: nodesWithFollowers\n",
    "Inputs: A dictionary\n",
    "Purpose:Returns a dictionary of nodes with followers (if given reverseNet)\n",
    "        or a list of friends (if given Net)\n",
    "\"\"\"\n",
    "def nodesWithFollowersorFriends(reverseNetwork):\n",
    "    hasFollowers ={}\n",
    "    for key,value in reverseNetwork.items():\n",
    "        hasFollowers[int(key)]=\"\"\n",
    "    return hasFollowers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "fb392210",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: identifyOutDegreeOfNodes\n",
    "Inputs: The nodes, reversed network, and a file to write to\n",
    "Purpose: Writes the out degree of each node to a file\n",
    "\"\"\"\n",
    "def identifyOutDegreeOfNodes(nodes,reverseNetwork,writefile=\"outDegree.txt\"):\n",
    "    d = {}\n",
    "    hasFollowers = nodesWithFollowersorFriends(reverseNetwork)\n",
    "\n",
    "    # Iterating through list of nodes\n",
    "    for key,value in nodes.items():\n",
    "        # if node has followers, add the amount of followers\n",
    "        if validKey(hasFollowers,int(key)):\n",
    "            d[int(key)] = len(reverseNetwork[key])\n",
    "        else:\n",
    "            d[int(key)] = 0\n",
    "\n",
    "    #writing to file\n",
    "    with open('backupFiles/{}'.format(writefile),'w') as convert_file:\n",
    "        convert_file.write(json.dumps(d))\n",
    "    convert_file.close()\n",
    "\n",
    "    return d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "89acace9",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: identifyInDegreeOfNodes\n",
    "Inputs: The nodes, network, and a file to write to\n",
    "Purpose: Writes the degree of each node to a file\n",
    "\"\"\"\n",
    "def identifyInDegreeOfNodes(nodes, Network, writefile=\"inDegree.txt\"):\n",
    "    d = {}\n",
    "    hasFriends = nodesWithFollowersorFriends(Network)\n",
    "\n",
    "    # Iterating through list of nodes\n",
    "    for key,value in nodes.items():\n",
    "        # if the node has friends, add the amount of friends\n",
    "        if validKey(hasFriends,int(key)):\n",
    "            d[int(key)] = len(Network[key])\n",
    "        else:\n",
    "            d[int(key)] = 0\n",
    "\n",
    "    #writing to file\n",
    "    with open('backupFiles/{}'.format(writefile), 'w') as convert_file:\n",
    "        convert_file.write(json.dumps(d))\n",
    "    convert_file.close()\n",
    "\n",
    "    return d"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6cdea18",
   "metadata": {},
   "source": [
    "## Part II- Identifying phrases"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "c2b34b79",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: titlesInNetwork\n",
    "Inputs: The nodes, papers, and a file to write to\n",
    "Purpose: Determines titles of papers in the network and\n",
    "         writes the results to a file\n",
    "\"\"\"\n",
    "def titlesInNetwork(nodes,papers,writefile=\"titlesInNetwork.txt\"):\n",
    "    with open('backupFiles/{}'.format(writefile),'w',encoding=\"utf8\") as f:\n",
    "        # Finds the titles with the hashtag and records the ID\n",
    "        for key, value in nodes.items():\n",
    "            f.write(papers[int(key)][1])\n",
    "            f.write(\"\\n\")\n",
    "    f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "29de1399",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: titlesCount\n",
    "Inputs: A file with a list of phrases\n",
    "Purpose: Determines how often a phrase appears in the network\n",
    "\"\"\"\n",
    "def titlesCount(phrases,titles=\"titlesInNetwork.txt\"):\n",
    "    counts = []\n",
    "    with open(phrases,'r',encoding=\"utf8\") as file1:\n",
    "        for hashtag in file1:\n",
    "            count = 0\n",
    "            hashtag = hashtag.lower().replace('\\n', '')\n",
    "            with open('backupFiles/{}'.format(titles),'r',encoding=\"utf8\") as file2:\n",
    "                for title in nonblankLines(file2):\n",
    "                    title = title.replace(\".\", \"\")\n",
    "                    title = title.replace(\",\", \"\")\n",
    "                    title = title.replace(\":\", \"\")\n",
    "                    title = title.replace(\"\\\"\", \"\")\n",
    "                    title = title.replace(\"!\", \"\")\n",
    "                    title = title.replace(\"!\", \"\")\n",
    "                    title = title.replace(\"“\", \"\")\n",
    "                    title = title.replace(\"‘\", \"\")\n",
    "                    title = title.replace(\"*\", \"\")\n",
    "                    if hashtag in title:\n",
    "                        count = count+1\n",
    "                file2.close()\n",
    "            counts.append(count)\n",
    "            print(hashtag, \"appears \", count, \" times\")\n",
    "        file1.close()\n",
    "\n",
    "    with open(\"backupFiles/totalCounts.txt\", 'w') as f:\n",
    "        for item in counts:\n",
    "            f.write(\"%s\\n\" % item)\n",
    "        f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "5fc8c667",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: findKeyWords\n",
    "Inputs: A filename, a string, and the number\n",
    "        of the words. The string is not case sensitive;\n",
    "        possbilities are \"Least\", \"Most\", or \"Average\n",
    "Purpose: To determine the most/least popular words\n",
    "\"\"\"\n",
    "def findKeyWords(name,popularity,n_print,writefile=\"keywords.txt\"):\n",
    "\n",
    "    #List of words\n",
    "    listOfWords = []\n",
    "\n",
    "    # Stopwords\n",
    "    stopwords = set(line.strip() for line in open('Stopwords.txt'))\n",
    "\n",
    "    # Instantiate a dictionary, and for every word in the file,\n",
    "    # Add to the dictionary if it doesn't exist. If it does, increase the count.\n",
    "    wordcount = {}\n",
    "    with open('backupFiles/{}'.format(name),'r',encoding=\"utf8\") as infile:\n",
    "        for lines in infile:\n",
    "                # To eliminate duplicates, remember to split by punctuation, and use case demiliters.\n",
    "                for word in lines.lower().split():\n",
    "                    word = word.replace(\".\", \"\")\n",
    "                    word = word.replace(\",\", \"\")\n",
    "                    word = word.replace(\":\", \"\")\n",
    "                    word = word.replace(\"\\\"\", \"\")\n",
    "                    word = word.replace(\"!\", \"\")\n",
    "                    word = word.replace(\"“\", \"\")\n",
    "                    word = word.replace(\"‘\", \"\")\n",
    "                    word = word.replace(\"*\", \"\")\n",
    "                    if word not in stopwords:\n",
    "                        if word not in wordcount:\n",
    "                            wordcount[word] = 1\n",
    "                        else:\n",
    "                            wordcount[word] += 1\n",
    "\n",
    "    # Close the file\n",
    "    infile.close()\n",
    "\n",
    "    #updates word_counter so that only words with 200+ uses are counted\n",
    "    word_counter = collections.Counter(wordcount)\n",
    "    for k in list(word_counter.keys()):\n",
    "        if word_counter[k] < 500: #orginally was 200\n",
    "            del word_counter[k]\n",
    "\n",
    "    #Handles the popularity metric\n",
    "    with open('backupFiles/{}'.format(writefile),'w',encoding=\"utf8\") as file:\n",
    "        if popularity.lower() == \"most\":\n",
    "            file.write(\"The {} most common words are \\n\".format(n_print))\n",
    "            for word, count in word_counter.most_common(n_print):\n",
    "                data = str(word)+\": \"+str(count)+\"\\n\"\n",
    "                file.write(data)\n",
    "                listOfWords.append(word)\n",
    "\n",
    "        elif popularity.lower() == \"least\":\n",
    "            file.write(\"The {} least common words are \\n\".format(n_print))\n",
    "            for word, count in word_counter.most_common()[:-n_print - 1:-1]:\n",
    "                data = str(word)+\": \"+str(count)+\"\\n\"\n",
    "                file.write(data)\n",
    "                listOfWords.append(word)\n",
    "\n",
    "        elif popularity.lower() == \"average\":\n",
    "            file.write(\"{} words in the middle are \\n\".format(n_print))\n",
    "            middle = math.ceil(len(word_counter)/2)\n",
    "            beginning = middle-(n_print/2)\n",
    "            end = middle+(n_print/2)\n",
    "            for word, count in word_counter.most_common()[int(beginning):int(end):1]:\n",
    "                data = str(word)+\": \"+str(count)+\"\\n\"\n",
    "                file.write(data)\n",
    "                listOfWords.append(word)\n",
    "\n",
    "    return listOfWords"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "c1161174",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: findKeyPhrases\n",
    "Inputs: A filename, a string, and the number\n",
    "        of the phrases. The string is not case sensitive;\n",
    "        possbilities are \"Least\", \"Most\", or \"Average\n",
    "Purpose: To determine the most/least popular phrases\n",
    "\"\"\"\n",
    "def findKeyPhrases(popularity,n_print,filename=\"titlesInNetwork.txt\",writefile=\"keyphrases.txt\"):\n",
    "\n",
    "    popularWords = findKeyWords(filename,popularity,n_print) \n",
    "    with open('backupFiles/{}'.format(writefile),'w',encoding=\"utf8\") as file:\n",
    "        #iterates through common words\n",
    "        for i in range(len(popularWords)):\n",
    "            with open('backupFiles/{}'.format(filename), 'r', encoding=\"utf8\") as f:\n",
    "                tokens = f.read().split()\n",
    "\n",
    "            bigram_measures = nltk.collocations.BigramAssocMeasures()\n",
    "\n",
    "            word_filter = lambda *w: popularWords[i] not in w\n",
    "\n",
    "            ## Bigrams\n",
    "            finder = BigramCollocationFinder.from_words(tokens)\n",
    "\n",
    "            # only bigrams that appear 3+ times\n",
    "            finder.apply_freq_filter(100) #originally 3\n",
    "\n",
    "            # only bigrams that contain the popular word\n",
    "            finder.apply_ngram_filter(word_filter)\n",
    "\n",
    "            # return the 5 n-grams with the highest PMI; could change the n-grams value to vary n phrases per word\n",
    "            data = str(finder.nbest(bigram_measures.likelihood_ratio, 10)) + \"\\n\" #originally was 5\n",
    "            file.writelines(data)\n",
    "\n",
    "        #closes titles file\n",
    "        f.close()\n",
    "    #closes keyphrase file\n",
    "    file.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1463afb9",
   "metadata": {},
   "source": [
    "## Part III- Identifying shared/exposed nodes and performing estimation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "a3fceb86",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\" \n",
    "Name: identifySharingNodes\n",
    "Inputs: A common phrase, the list of papers,nodes, a file\n",
    "Purpose: Writes the sharing status of each node to a file\n",
    "\"\"\"\n",
    "def identifySharingNodes(phrase, papers, nodes, writefile=\"sharingNodes.txt\"):\n",
    "    shared = {}\n",
    "\n",
    "    #Finds the titles with the hashtag and records the ID\n",
    "    for key,value in nodes.items():\n",
    "        title = papers[int(key)][1]\n",
    "        if phrase in title:\n",
    "            shared[int(key)] = 1\n",
    "        else:\n",
    "            shared[int(key)] = 0\n",
    "\n",
    "    #writing to file\n",
    "    with open('backupFiles/{}'.format(writefile),'w') as convert_file:\n",
    "        convert_file.write(json.dumps(shared))\n",
    "\n",
    "    return shared"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "488429ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Name: identifyExposedNodes\n",
    "Inputs: a list of sharers, the reversed network\n",
    "Purpose: To create a list of followers that were exposed\n",
    "\"\"\"\n",
    "def identifyExposedNodes(shared,forwardNetwork,writefile=\"exposedNodes.txt\"):\n",
    "    exposed = {}\n",
    "\n",
    "    for key,value in shared.items():\n",
    "        if shared[int(key)] == 1: #if the node shared, then it's exposed\n",
    "            exposed[int(key)] = 1\n",
    "        elif validKey(forwardNetwork,str(key)): #if the node follows people\n",
    "            friends = list(forwardNetwork[str(key)])\n",
    "            if search(friends,shared): #search to see if friend shared\n",
    "                exposed[int(key)] = 1 #if friend shared, then exposed\n",
    "            else:\n",
    "                exposed[int(key)] = 0\n",
    "        else: #if node didn't share, nor followed someone who shared, then not exposed\n",
    "            exposed[int(key)] = 0\n",
    "\n",
    "    #writing to file\n",
    "    with open('backupFiles/{}'.format(writefile),'w') as convert_file:\n",
    "        convert_file.write(json.dumps(exposed))\n",
    "\n",
    "    return exposed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "6fdd0d8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Nodes : List of nodes [1,2,3....]\n",
    "# d_out : d_out[i] is the out-degree (number of papers that cited paper i)\n",
    "# d_in : d_in[i] is the in-degree (number of papers that paper i cited)\n",
    "# s : s[i] = 1 if i is shared, s[i] = 0 otherwise\n",
    "# f : f[i] = 1 if i is exposed, f[i] = 0 otherwise (This has to be constructed)\n",
    "\n",
    "def MeanAbsError(n_list, No_iterations = 100):\n",
    "    #Dictionary for storing the results for various n values\n",
    "    Dict_Results = {}\n",
    "    \n",
    "    #Creates a dictionary with the node # as the key and the index as the value\n",
    "    hashtable = createHashtable(Nodes)\n",
    "  \n",
    "    # The following for-loop considers different values of the sample size\n",
    "    for n in n_list:\n",
    "\n",
    "        #Vectors to store the estimates from the two methods\n",
    "        Estimate_X_vec_error = []\n",
    "        Estimate_Y_vec_error = []\n",
    "        Estimate_Z_vec_error = []       \n",
    "\n",
    "        i = 0\n",
    "        # The following while loop generates vanilla and friendship paradox based MLEs in each iteration        \n",
    "        while i < No_iterations: \n",
    "\n",
    "            # Sampling n Random Nodes (X_1, X_2,....X_n) independently from the set of nodes of the network G\n",
    "            X_nodes = list(np.random.choice(Nodes, size=n))\n",
    "\n",
    "            # Sampling n Random Friends (Y_1, Y_2,....Y_n) independently (version-1 of friendship paradox)\n",
    "            Y_nodes = list(np.random.choice(Nodes, size=n, p = np.array(d_out)/sum_degree_out)) \n",
    "            \n",
    "            # Sampling n Random Followers (Y_1, Y_2,....Y_n) independently (version-2 of friendship paradox)\n",
    "            Z_nodes = list(np.random.choice(Nodes, size=n, p = np.array(d_in)/sum_degree_in)) \n",
    "        \n",
    "            # Computing the vanilla estimate\n",
    "            print(\"Iteration: \", i,\"/\",No_iterations, \"  in List\",n,\"/\",n_list)\n",
    "            f_hat_vanilla = sum([f[hashtable[v]] for v in X_nodes])/n\n",
    "            Estimate_X_vec_error.append(np.abs(f_hat_vanilla - bar_f)*100/bar_f)\n",
    "\n",
    "            # Computing the friendship paradox based method (followers)\n",
    "            f_hat_FP1 = sum([(bar_d * f[hashtable[v]]/d_out[hashtable[v]]) for v in Y_nodes])/n\n",
    "            Estimate_Y_vec_error.append(np.abs(f_hat_FP1 - bar_f)*100/bar_f)\n",
    "            \n",
    "            # Computing the friendship paradox based method (friends)\n",
    "            f_hat_FP2 = sum([(bar_d * f[hashtable[v]]/d_in[hashtable[v]]) for v in Z_nodes])/n\n",
    "            Estimate_Z_vec_error.append(np.abs(f_hat_FP2 - bar_f)*100/bar_f)\n",
    "            \n",
    "            i = i + 1\n",
    "\n",
    "        #Storing the values in a dictionary in compact form\n",
    "        Dict_Results[n] = (n, bar_f, np.corrcoef(d_out, s)[0,1], np.mean(Estimate_X_vec_error), np.mean(Estimate_Y_vec_error),np.mean(Estimate_Z_vec_error))\n",
    "        \n",
    "    return Dict_Results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "01a31177",
   "metadata": {},
   "outputs": [],
   "source": [
    "## Generating the results and saving\n",
    "def SaveError(G, s, GraphName):\n",
    "\n",
    "    n_list = [int(np.ceil(x*len(G.nodes()))) for x in [0.001, 0.002, 0.003, 0.004, 0.005]]\n",
    "            \n",
    "    Dict_Name = 'Results/' + GraphName\n",
    "    Dict_Results = MeanAbsError(n_list, No_iterations = 5000)    \n",
    "\n",
    "    try:\n",
    "        import cPickle as pickle\n",
    "    except ImportError:  \n",
    "        import pickle\n",
    "    with open(Dict_Name, 'wb') as fp:\n",
    "       pickle.dump(Dict_Results, fp, protocol=pickle.HIGHEST_PROTOCOL)\n",
    "\n",
    "    (n, bar_f, p_ks, Vanilla_error, FP_error_1,FP_error_2) = zip(*list(Dict_Results.values()))               \n",
    "    print(GraphName + r'$r_{kk}$ = ' + str(nx.degree_assortativity_coefficient(G)))\n",
    "    print('p_ks (actual)= ' + str(p_ks[0]))    \n",
    "    print(r'$\\bar{f} = $' + str(bar_f[0]))                \n",
    "    print('')                "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "b66744a9",
   "metadata": {},
   "outputs": [],
   "source": [
    "## Loading the results and plotting\n",
    "def PlotError(G,GraphName):\n",
    "    %matplotlib inline\n",
    "    import matplotlib.pylab as pl\n",
    "\n",
    "    matplotlib.rc('xtick', labelsize=4) \n",
    "    matplotlib.rc('ytick', labelsize=4) \n",
    "\n",
    "    MARKERS = ['s','o','x']\n",
    "\n",
    "    n_list = [int(np.ceil(x*len(G.nodes()))) for x in [0.001, 0.002, 0.003, 0.004, 0.005]]\n",
    "    \n",
    "    Dict_Name = 'Results/' + GraphName\n",
    "\n",
    "    try:\n",
    "        import cPickle as pickle\n",
    "    except ImportError:  \n",
    "        import pickle\n",
    "    with open(Dict_Name, 'rb') as fp:\n",
    "        Dict_Results = pickle.load(fp)            \n",
    "\n",
    "    (n, bar_f, p_ks, Vanilla_error, FP_error_1,FP_error_2) = zip(*list(Dict_Results.values()))\n",
    "    plt.plot(n, Vanilla_error, #For percentage error                                         \n",
    "                  label = r'$\\hat{f}_{\\mathrm{vl}}$',\n",
    "                  color = 'tab:grey', \n",
    "                  marker = 's',\n",
    "                  markersize = 3.5,  \n",
    "                  markerfacecolor = 'none',                          \n",
    "                  linestyle = '--',\n",
    "                  linewidth = 0.75                      \n",
    "                 )\n",
    "\n",
    "    plt.plot(n, FP_error_1, #For percentage error                           \n",
    "                  label = r'$\\hat{f}_{\\mathrm{FP}}$',\n",
    "                  color = 'tab:olive',\n",
    "                  marker = 'o',  \n",
    "                  markersize = 3.5,      \n",
    "                  markerfacecolor = 'none',                        \n",
    "                  linestyle = ':' ,                         \n",
    "                  linewidth = 0.75\n",
    "                 )   \n",
    "    \n",
    "    plt.plot(n, FP_error_2, #For percentage error                           \n",
    "                  label = r'$\\hat{f}_{\\mathrm{FP}}$',\n",
    "                  color = 'tab:purple',\n",
    "                  marker = 'x',  \n",
    "                  markersize = 3.5,      \n",
    "                  markerfacecolor = 'none',                        \n",
    "                  linestyle = ':' ,                         \n",
    "                  linewidth = 0.75\n",
    "                 )  \n",
    "\n",
    "    print(GraphName + r'$r_{kk}$ = ' + str(nx.degree_assortativity_coefficient(G)))                       \n",
    "    print('p_ks (actual)= ' + str(p_ks[0]))    \n",
    "    print(r'$\\bar{f} = $' + str(bar_f[0]))                \n",
    "    print('')                \n",
    "\n",
    "    # Setting the x and y labels    \n",
    "    plt.xlabel(r'Sample size $n$', fontsize=15, labelpad=1)\n",
    "    plt.ylabel(r'Absolute Error (%)', fontsize=15, labelpad=1)\n",
    "    plt.xticks(fontsize= 15)\n",
    "    plt.yticks(fontsize= 15)\n",
    "    plt.ylim([0,260])\n",
    "    plt.legend(loc='upper center', bbox_to_anchor=(0.35, 1.2),ncol=3)\n",
    "    plt.gcf().text(0.67, 0.87, textstr, fontsize=12)  \n",
    "    plt.tight_layout()  \n",
    "    \n",
    "    #Saving the plot\n",
    "    GraphName = GraphName.replace(\".txt\",\".png\")\n",
    "    name = 'Results/Figures/{}'.format(GraphName)\n",
    "    plt.savefig(name)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "efd6a35f",
   "metadata": {},
   "outputs": [],
   "source": [
    "#Takes all the files in the folder and determines the average with error bars\n",
    "def PlotErrorAveragewith_error_bars(G,directory):\n",
    "    %matplotlib inline\n",
    "    import matplotlib.pylab as pl\n",
    "\n",
    "    matplotlib.rc('xtick', labelsize=4) \n",
    "    matplotlib.rc('ytick', labelsize=4) \n",
    "\n",
    "    MARKERS = ['s','o','x']\n",
    "    fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(7.0, 1.8))\n",
    "\n",
    "    n_list = [int(np.ceil(x*len(G.nodes()))) for x in [0.001, 0.002, 0.003, 0.004, 0.005]]\n",
    "    j = 0\n",
    "    \n",
    "    #walking through folder\n",
    "    for rootdir, dirs, files in os.walk(directory):\n",
    "        #walking through subfolder\n",
    "        for subdir in dirs:\n",
    "            \n",
    "            #resetting the dictionaries\n",
    "            f ={}\n",
    "            pks = {}\n",
    "            vanilla ={}\n",
    "            fp1 ={}\n",
    "            fp2 ={}\n",
    "            path = os.path.join(directory, subdir)\n",
    "            \n",
    "            #getting files in subfolder\n",
    "            for (dirpath, dirnames, filenames) in os.walk(path):\n",
    "                for file in filenames:\n",
    "                    try:\n",
    "                        import cPickle as pickle\n",
    "                    except ImportError:  \n",
    "                        import pickle\n",
    "\n",
    "                    with open(os.path.join(dirpath, file), \"rb\") as fp:\n",
    "                        Dict_Results = pickle.load(fp)\n",
    "                        (n, bar_f, p_ks, Vanilla_error, FP_error_1,FP_error_2) = zip(*list(Dict_Results.values()))\n",
    "\n",
    "                        #updating the dictionary\n",
    "                        for i in range(len(n)):\n",
    "                            if n[i] in f:\n",
    "                                f[n[i]].append(bar_f[i])\n",
    "                                pks[n[i]].append(p_ks[i])\n",
    "                                vanilla[n[i]].append(Vanilla_error[i])\n",
    "                                fp1[n[i]].append(FP_error_1[i])\n",
    "                                fp2[n[i]].append(FP_error_2[i])\n",
    "\n",
    "                            else:\n",
    "                                f[n[i]] = [bar_f[i]]\n",
    "                                pks[n[i]] = [p_ks[i]]\n",
    "                                vanilla[n[i]] = [Vanilla_error[i]]\n",
    "                                fp1[n[i]] = [FP_error_1[i]]\n",
    "                                fp2[n[i]] = [FP_error_2[i]]\n",
    "\n",
    "            vanilla_mean,vanilla_error,fp1_mean,fp1_error,fp2_mean,fp2_error =([] for i in range(6))\n",
    "    \n",
    "            #Determining averages and standard deviation for each point\n",
    "            for i in range(len(n_list)):\n",
    "                vanilla_mean.append(np.nanmean(vanilla[n_list[i]]))\n",
    "                vanilla_error.append(np.nanstd(vanilla[n_list[i]]))\n",
    "                fp1_mean.append(np.nanmean(fp1[n_list[i]]))\n",
    "                fp1_error.append(np.nanstd(fp1[n_list[i]]))\n",
    "                fp2_mean.append(np.nanmean(fp2[n_list[i]]))\n",
    "                fp2_error.append(np.nanstd(fp2[n_list[i]]))\n",
    "\n",
    "            ax[j].errorbar(n, vanilla_mean, #For percentage error \n",
    "                          yerr = vanilla_error,\n",
    "                          label = r'$\\hat{f}_{\\mathrm{vl}}$',\n",
    "                          color = 'tab:red', \n",
    "                          marker = 's',\n",
    "                          markersize = 3.5,  \n",
    "                          markerfacecolor = 'none',                          \n",
    "                          linestyle = '--',\n",
    "                          linewidth = 0.75                      \n",
    "                         )\n",
    "\n",
    "            ax[j].errorbar(n, fp1_mean, #For percentage error  \n",
    "                          yerr = fp1_error,\n",
    "                          label = r'$\\hat{f}_{\\mathrm{FP}}$',\n",
    "                          color = 'tab:green',\n",
    "                          marker = 'o',  \n",
    "                          markersize = 3.5,      \n",
    "                          markerfacecolor = 'none',                        \n",
    "                          linestyle = ':' ,                         \n",
    "                          linewidth = 0.75\n",
    "                         )   \n",
    "\n",
    "            ax[j].errorbar(n, fp2_mean, #For percentage error \n",
    "                          yerr = fp2_error,\n",
    "                          label = r'$\\hat{f}_{\\mathrm{FO}}$',\n",
    "                          color = 'tab:blue',\n",
    "                          marker = 'x',  \n",
    "                          markersize = 3.5,      \n",
    "                          markerfacecolor = 'none',                        \n",
    "                          linestyle = '-.' ,                         \n",
    "                          linewidth = 0.75\n",
    "                         ) \n",
    "            handles,labels= ax[j].get_legend_handles_labels()\n",
    "            j+=1\n",
    "    \n",
    "    # Setting the x and y labels \n",
    "    for AX in ax.flat:\n",
    "        AX.set_xlabel(r'Sample size $n$', fontsize=12, labelpad=1)\n",
    "        AX.set_ylabel(r'Abs. Error(%)', fontsize=12, labelpad=2)\n",
    "        AX.xaxis.set_tick_params(labelsize=10)\n",
    "        AX.yaxis.set_tick_params(labelsize=10) \n",
    "        AX.set_ylim([0,150])\n",
    "        AX.set_xticks([218, 435, 653, 870, 1087])\n",
    "\n",
    "    # Setting the row and column headers\n",
    "    cols = [\"Popular phrases\", \"Average phrases\", \"Unpopular phrases\"]\n",
    "    pad = 5\n",
    "    for AX, col in zip(ax, cols):\n",
    "        AX.annotate(col, xy=(0.5, 1.0), xytext=(0, pad+1),\n",
    "                    xycoords='axes fraction', textcoords='offset points',\n",
    "                    size=12, ha='center', va='baseline')\n",
    "    fig.tight_layout() \n",
    "        \n",
    "    # Adding panel numbers\n",
    "    panel_list = ['(a)', '(b)', '(c)']\n",
    "    panel_list_ind = 0\n",
    "    for AX in ax.flat:\n",
    "        AX.text(0.47, -0.35, panel_list[panel_list_ind], transform=AX.transAxes, fontsize=12, fontweight='bold', va='top')\n",
    "        panel_list_ind = panel_list_ind + 1\n",
    "\n",
    "    # Setting the legend and adjusting plots\n",
    "    fig.legend(handles, labels, loc='lower center', ncol=3, fontsize=12, bbox_to_anchor=(0.51, +.87), edgecolor = 'none')   \n",
    "    plt.subplots_adjust(bottom = 0.08, wspace=0.5, hspace=0.5)    \n",
    "    plt.savefig('Averages_updated.pdf', bbox_inches='tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "4555db24",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 504x129.6 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#Main\n",
    "\n",
    "#PART 1 Functions\n",
    "#Organizing the original data file\n",
    "# parseFile('outputacm.txt')\n",
    "\n",
    "#Creating the papers\n",
    "# papers = createPapers()\n",
    "\n",
    "#Creating the networks\n",
    "# net = createNetwork(papers)\n",
    "# revNet = createReverseNetwork(net)\n",
    "\n",
    "#Creating nodes & edgelist\n",
    "# nodes = identifyNodes(revNet)\n",
    "# createEdgelist(net)\n",
    "\n",
    "#Creating the graph\n",
    "graph = createGraph()\n",
    "\n",
    "#Determining in and out degree of nodes\n",
    "# d_out = identifyOutDegreeOfNodes(nodes,revNet)\n",
    "# d_in = identifyInDegreeOfNodes(nodes,net)\n",
    "\n",
    "#PART 2 Functions\n",
    "#Determining hashtags\n",
    "#titlesInNetwork(nodes,papers)\n",
    "#findKeyPhrases(\"average\",30)\n",
    "# phrases = getList('backupFiles/phrases.txt')\n",
    "# titlesCount('backupFiles/phrases.txt')\n",
    "\n",
    "#PART 3 Functions\n",
    "#Identifying the sharing nodes and exposed nodes\n",
    "# phrase = \"Access Control\"\n",
    "# phrase = phrase.lower()\n",
    "# shared = identifySharingNodes(phrase, papers, nodes)\n",
    "# exposed =identifyExposedNodes(shared, net)\n",
    "\n",
    "#Converting from dict to list\n",
    "# Nodes = list(nodes.keys())\n",
    "# d_out = list(d_out.values())\n",
    "# d_in = list(d_in.values())\n",
    "# s= list(shared.values())\n",
    "# f= list(exposed.values())\n",
    "\n",
    "# sum_degree_out = sum(d_out)\n",
    "# sum_degree_in = sum(d_in)\n",
    "# bar_d = sum(d_out)/len(d_out) \n",
    "# bar_f = sum(f)/len(f) \n",
    "\n",
    "#Performing estimation\n",
    "# SaveError(graph,s,'access_control.txt')\n",
    "# PlotError(graph,'access_control.txt')\n",
    "PlotErrorAveragewith_error_bars(graph,'newEstimates')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d83e783",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

GitHub Events

Total
Last Year