├── .gitignore ├── Bouquin_DeBlase_JupyterCon_2017.pdf ├── DATA661_final.ipynb ├── IPySig_demo_data ├── IPySig_demo_data.ipynb ├── stars_main_edges.csv ├── stars_main_nodes.csv ├── stars_test_graph.ipynb ├── stars_test_graph.pkl └── top_50.csv ├── LICENSE ├── README.md ├── app ├── browser │ ├── bower.json │ ├── client.js │ ├── custom.css │ ├── extra │ │ └── jquery.tabletoCSV.js │ ├── index.html │ └── sigma │ │ ├── sigma.layout.forceAtlas2.min.js │ │ └── sigma.min.js ├── index.js └── package.json ├── assets ├── css │ ├── 1-tools │ │ ├── _fonts.scss │ │ ├── _mixins.sass │ │ ├── _normalize.scss │ │ └── _vars.sass │ ├── 2-basics │ │ ├── _body-element.sass │ │ ├── _selection-colors.sass │ │ └── _typography.sass │ ├── 3-modules │ │ └── _nav.sass │ ├── 4-sections │ │ ├── _about.sass │ │ ├── _featured.sass │ │ ├── _full-slide.sass │ │ └── _hero.sass │ ├── fonts │ │ ├── icomoon.eot │ │ ├── icomoon.svg │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ ├── lmromanslant10-regular-webfont.eot │ │ ├── lmromanslant10-regular-webfont.svg │ │ ├── lmromanslant10-regular-webfont.ttf │ │ └── lmromanslant10-regular-webfont.woff │ ├── main.css │ └── main.sass ├── img │ ├── featured.png │ ├── flow.png │ ├── full-slide │ │ ├── thumb-1.jpg │ │ ├── thumb-2.jpg │ │ └── thumb-3.jpg │ ├── hero.png │ └── logos.png └── js │ ├── functions-min.js │ ├── functions.js │ └── vendor │ └── jquery-2.2.4.min.js ├── images └── ipysig_diagram1.png ├── index.html ├── ipysig ├── __init__.py ├── core.py ├── exceptions.py ├── sigma_addon_methods.py └── tests │ ├── __init__.py │ ├── mocks.py │ ├── test_core.py │ └── test_sigma_addon_methods.py ├── ipysig_test.ipynb ├── requirements.txt └── scripts ├── install_hooks.sh ├── pre_commit.sh └── run_tests.sh /.gitignore: -------------------------------------------------------------------------------- 1 | ########################### 2 | # Custom gitignore here 3 | ########################## 4 | *.db 5 | 6 | 7 | 8 | 9 | ########################## 10 | # Python gitignore here... 11 | ######################### 12 | 13 | 14 | # Byte-compiled / optimized / DLL files 15 | __pycache__/ 16 | *.py[cod] 17 | *$py.class 18 | 19 | # C extensions 20 | *.so 21 | 22 | # Distribution / packaging 23 | .Python 24 | env/ 25 | build/ 26 | develop-eggs/ 27 | dist/ 28 | downloads/ 29 | eggs/ 30 | .eggs/ 31 | lib/ 32 | lib64/ 33 | parts/ 34 | sdist/ 35 | var/ 36 | *.egg-info/ 37 | .installed.cfg 38 | *.egg 39 | 40 | # PyInstaller 41 | # Usually these files are written by a python script from a template 42 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 43 | *.manifest 44 | *.spec 45 | 46 | # Installer logs 47 | pip-log.txt 48 | pip-delete-this-directory.txt 49 | 50 | # Unit test / coverage reports 51 | htmlcov/ 52 | .tox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *,cover 59 | .hypothesis/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # IPython Notebook 83 | .ipynb_checkpoints 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # celery beat schedule file 89 | celerybeat-schedule 90 | 91 | # dotenv 92 | .env 93 | 94 | # virtualenv 95 | venv/ 96 | ENV/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | ################################# 105 | # BEGIN NODE.js gitignore here::: 106 | ################################## 107 | 108 | 109 | # Logs 110 | logs 111 | *.log 112 | npm-debug.log* 113 | yarn-debug.log* 114 | yarn-error.log* 115 | 116 | # Runtime data 117 | pids 118 | *.pid 119 | *.seed 120 | *.pid.lock 121 | 122 | # Directory for instrumented libs generated by jscoverage/JSCover 123 | lib-cov 124 | 125 | # Coverage directory used by tools like istanbul 126 | coverage 127 | 128 | # nyc test coverage 129 | .nyc_output 130 | 131 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 132 | .grunt 133 | 134 | # Bower dependency directory (https://bower.io/) 135 | bower_components 136 | 137 | # node-waf configuration 138 | .lock-wscript 139 | 140 | # Compiled binary addons (http://nodejs.org/api/addons.html) 141 | build/Release 142 | 143 | # Dependency directories 144 | node_modules/ 145 | jspm_packages/ 146 | 147 | # Typescript v1 declaration files 148 | typings/ 149 | 150 | # Optional npm cache directory 151 | .npm 152 | 153 | # Optional eslint cache 154 | .eslintcache 155 | 156 | # Optional REPL history 157 | .node_repl_history 158 | 159 | # Output of 'npm pack' 160 | *.tgz 161 | 162 | # Yarn Integrity file 163 | .yarn-integrity 164 | 165 | # dotenv environment variables file 166 | .env 167 | 168 | -------------------------------------------------------------------------------- /Bouquin_DeBlase_JupyterCon_2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/Bouquin_DeBlase_JupyterCon_2017.pdf -------------------------------------------------------------------------------- /DATA661_final.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Final - DATA 661\n", 8 | " \n", 9 | "## IPySigma Lives: Presenting an Extensible Prototype Network Visualization Frontend for Jupyter Notebooks\n", 10 | "\n", 11 | "### John DeBlase, Daina Bouquin\n", 12 | " \n", 13 | "#### Introduction\n", 14 | "The potential uses of network analytics and visualizations are extensive, with applications ranging from social network analysis to environmental science to better understanding how political revolutions spread. However, many of the tools most commonly used for these types of analysis, particularly Python modules like NetworkX, are not designed to produce aesthetically pleasing, interactive visualizations that support the development of theories and inferences. Much of the time, data scientists using tools like Jupyter are left trying to work with network visualizations that look like hairballs or spending a great deal of time trying to use unfamiliar tools like JavaScript or Gephi to produce useful graphs. These people do not currently have access to simple interfaces that integrate with tools like Jupyter notebooks (formerly IPython) that the rest of their workflows rely upon. \n", 15 | " \n", 16 | "Throughout this semester, we thought through this problem and began prototyping a flexible architecture to help people create SigmaJS networks from NetworkX objects without abandoning their Jupyter Notebooks. The result is an extensible proof-of-concept for a side-by-side Jupyter network visualization GUI that will allow users to quickly create clear visualizations that can help drive research processes. \n", 17 | " \n", 18 | "The below sections aim to justify the use of Jupyter as a platform, the focus of the application, the components selected to create the application, and to outline steps that can be taken moving forward to improve and expand on the current infrastructure. \n", 19 | "#### Why Jupyter?\n", 20 | "Jupyter is becoming increasingly important to the data community for sharing and reproducibility, therefore tools that integrate with this environment are highly valuable. Reproducibility is also foundationally important to computational science endeavors more broadly, both in academia and in industry. \n", 21 | "Why Focus on Network Visualization?\n", 22 | "Visualization is integral to the data scientist’s ability to use network analytics to effectively derive theories and inferences. Research in social network analysis has shown that dynamic and interactive graph visualizations foster “theoretical insight” thus creating a real need for “dynamic network visualizations” [[1](http://www.journals.uchicago.edu.ezp-prod1.hul.harvard.edu/doi/full/10.1086/421509)]. Moreover, many scientific domains are “now convinced that network visualization is essential to improve their work since it allows them to see complex structures that statistics and modeling alone cannot reveal” [[2]( http://www.msr-inria.fr/projects/interactive-network-visualiation/)].\n", 23 | "#### Why SigmaJS?\n", 24 | "SigmaJS is a JavaScript library dedicated to graph drawing. Unlike libraries like D3, Sigma is optimized for our usecase.\n", 25 | "\n", 26 | "#### Evaluation\n", 27 | "Over the 15 weeks of the CUNY SPS spring semester, an iterative, stepped approach was taken with the goal being to build a functional prototype to with the above described functionality. We were able to achive this goal, and also able to achieve our stretch goal by being accepted as speakers at [JupyterCon 2017](https://conferences.oreilly.com/jupyter/jup-ny), the Jupyter Project’s first international conference." 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "### Running the IPySigma prototype\n", 35 | "\n", 36 | "### Manual Install:\n", 37 | "\n", 38 | "git clone [this](https://github.com/bsnacks000/IPySigma-Demo) repo and install both the python and node components.\n", 39 | "\n", 40 | "#### Python\n", 41 | "\n", 42 | "The prototype python package is contained in the \"ipysig\" folder.\n", 43 | "\n", 44 | "1. From the root directory: Build and activate a clean python environment>=2.7.10 with requirements.txt using [virtualenv](https://virtualenv.pypa.io/en/stable/).\n", 45 | "\n", 46 | "2. `pip install -r requirements.txt` to get the required packages.\n", 47 | "\n", 48 | "#### Node.js\n", 49 | "\n", 50 | "1. The node-express application is contained in the app folder.\n", 51 | "\n", 52 | "*Make sure your node version is >= v6.9.4 and that both `npm` and `bower` are installed globally.*\n", 53 | "\n", 54 | "2. From the root directory: `cd ./app`\n", 55 | "\n", 56 | "3. type `npm install` to install the node modules locally in the app top-level folder\n", 57 | "\n", 58 | "4. From app: `cd ./browser`\n", 59 | "\n", 60 | "5. type `bower install` to install the bower_components folder (note: these steps might change in future versions with browserify)\n", 61 | "\n", 62 | "#### Run the Demo\n", 63 | "\n", 64 | "At the root directory launch a jupyter notebook server and run the notebook ipysig_test.ipynb and then follow the instructions for each cell." 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "### Overview of components\n", 72 | "" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "### Overview of IPySigma Application System\n", 80 | "\n", 81 | "#### Jupyter Notebook Frontend/ Jupyter Notebook Server\n", 82 | "\n", 83 | "To boot up the app using a running notebook server, first the IPySig controller class is imported and instantiated within the user’s notebook session. This is the core Python class of the application and is implemented as a Singleton. An IPySig object does a few key things on instantiation:\n", 84 | "If run for the first time, IPySig injects the API found in the ipysig.sigma_addon_methods into the nx.Graph class. This adds custom functionality to any existing or newly created graphs in the current session for exporting node and edge data in JSON format suitable for SigmaJS. \n", 85 | "Next, using a system call it fetches and stores the url and token (if any) of the running notebook server. \n", 86 | "Using the url and token, an express server process is spun up and stored in a class variable. This is run through several sets of protected method calls.\n", 87 | "\n", 88 | "Once the express server is running, a “session” can be created by calling the connect() method passing in the graph object and a key name. The graph object reference gets stored in IPySig under this key name. The key name is also emitted via a socket.io client to a listener on the express server which stores the reference in node.js and sends a callback response that tells IPySig to spin up a webbrowser tab. When the browser tab gets opened an event is emitted to fetch the key name to the graph reference, thus completing the connection between the new browser tab and the running graph object in the IPython kernel.\n", 89 | "\n", 90 | "A session has now been created and the user can use the frontend to display the graph object using the Load Graph button and giving the graph a Title. The title will serve as a means of saving a graph in BrowserDB via a LokiJS adaptor, allowing persistence of multiple graph objects in a single browser session. This functionality has not been implemented yet as of version 0.1.\n", 91 | "\n", 92 | "#### Jupyterlab Services/Express/SigmaJS \n", 93 | "\n", 94 | "Once a browser tab session is bound, pressing Load Graph and submitting a Title will emit to the ‘main-room’ socket.io listener on the express server. The jupyterlab services API connects to the running kernel and calls IPySig.export_graph_instance() with the correct graph_name as a promise. \n", 95 | "\n", 96 | "Once the promise is fulfilled, the JSON graph data is emitted back up to the browser via the correct socket.id. The graph data is then rendered in the browser main_room listener via a call to make_graph which returns the sigma graph.\n", 97 | "\n", 98 | "In future releases, each new graph will be saved to BrowserDB via a Loki.js adaptor. The user will be able to select from a list of past graphs rendered in that session for easy comparison. The option will also be given for the contents of the session to be exported to JSON.\n" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "### The Future" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "There are a number of avenues that could be explored using the protocol and platform defined above. Over the next few months, Daina and John will define an applied usecase to be presented at JupyterCon along with a presentation on IPySigma's development and logic. Improvements will also be made to the code to address a number of issues currently [documented on GitHub](https://github.com/bsnacks000/IPySigma-Demo/issues). Additionally, documentation will be automated using [Sphinx](http://www.sphinx-doc.org/en/stable/) to improve the seamlessness of updates and allow us to create more professional web presence. We have also begun creating a [website](https://dbouquin.github.io/IPySigma-Demo/) that will act as a splash page for the tool." 113 | ] 114 | } 115 | ], 116 | "metadata": { 117 | "anaconda-cloud": {}, 118 | "kernelspec": { 119 | "display_name": "Python [Root]", 120 | "language": "python", 121 | "name": "Python [Root]" 122 | }, 123 | "language_info": { 124 | "codemirror_mode": { 125 | "name": "ipython", 126 | "version": 2 127 | }, 128 | "file_extension": ".py", 129 | "mimetype": "text/x-python", 130 | "name": "python", 131 | "nbconvert_exporter": "python", 132 | "pygments_lexer": "ipython2", 133 | "version": "2.7.9" 134 | } 135 | }, 136 | "nbformat": 4, 137 | "nbformat_minor": 0 138 | } 139 | -------------------------------------------------------------------------------- /IPySig_demo_data/IPySig_demo_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Data pull from ADS\n", 8 | "Focusing in on 50 most highly cited astronomy papers from the last 5 years" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 7, 14 | "metadata": {}, 15 | "outputs": [], 16 | "source": [ 17 | "import networkx as nx\n", 18 | "import os\n", 19 | "import ads as ads \n", 20 | "import matplotlib.pyplot as plt\n", 21 | "import pandas as pd\n", 22 | "from networkx.algorithms import bipartite as bi" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 8, 28 | "metadata": { 29 | "collapsed": true 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "os.environ[\"ADS_DEV_KEY\"] = \"kNUoTurJ5TXV9hsw9KQN1k8wH4U0D7Oy0CJoOvyw\"" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 9, 39 | "metadata": { 40 | "collapsed": true 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "ads.config.token = 'ADS_DEV_KEY' " 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 10, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "#Search for papers \n", 54 | "# 50 most cited papers recent papers\n", 55 | "papers1 = list(ads.SearchQuery(q=\"*\",\n", 56 | " sort=\"citation_count desc\",\n", 57 | " year=\"2012, 2013, 2014, 2015, 2016, 2017\",\n", 58 | " rows=50 ))" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 11, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "# find author names\n", 68 | "a = []\n", 69 | "for i in papers1:\n", 70 | " authors1 = i.author\n", 71 | " a.append(authors1)\n", 72 | "author_names = a" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 12, 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "name": "stderr", 82 | "output_type": "stream", 83 | "text": [ 84 | "/home/bsnacks/Py_projects/ipysigma_demo/venv/lib/python2.7/site-packages/ads/utils.py:31: UserWarning: You are lazy loading attributes via 'pub', and so are making multiple calls to the API. This will impact your overall rate limits.\n", 85 | " UserWarning,\n" 86 | ] 87 | } 88 | ], 89 | "source": [ 90 | "# find the journals\n", 91 | "j = []\n", 92 | "for i in papers1:\n", 93 | " journals1 = i.pub\n", 94 | " j.append(journals1)\n", 95 | "journals = j" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 13, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "# find year of publication\n", 105 | "y = []\n", 106 | "for i in papers1:\n", 107 | " year1 = i.year\n", 108 | " y.append(year1)\n", 109 | "year = y" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 14, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "# find titles\n", 119 | "t = []\n", 120 | "for i in papers1:\n", 121 | " title1 = i.title\n", 122 | " t.append(title1)\n", 123 | "title = t" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 15, 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "name": "stderr", 133 | "output_type": "stream", 134 | "text": [ 135 | "/home/bsnacks/Py_projects/ipysigma_demo/venv/lib/python2.7/site-packages/ads/utils.py:31: UserWarning: You are lazy loading attributes via 'keyword', and so are making multiple calls to the API. This will impact your overall rate limits.\n", 136 | " UserWarning,\n" 137 | ] 138 | } 139 | ], 140 | "source": [ 141 | "# find keywords\n", 142 | "k = []\n", 143 | "for i in papers1:\n", 144 | " keyword1 = i.keyword\n", 145 | " k.append(keyword1)\n", 146 | "keyword = k" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 16, 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "# create an initial df\n", 156 | "df = pd.DataFrame({'Author_Names' : author_names,\n", 157 | " 'Journal':journals,\n", 158 | " 'Year':year,\n", 159 | " 'Title':title,\n", 160 | " 'Keyword':keyword\n", 161 | " })" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 17, 167 | "metadata": {}, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "(34752, 5)\n" 174 | ] 175 | } 176 | ], 177 | "source": [ 178 | "# Expand the df with melt\n", 179 | "s1 = df.apply(lambda x: pd.Series(x['Author_Names']),axis=1).stack().reset_index(level=1, drop=True)\n", 180 | "s1.name = 'Author_Name'\n", 181 | "s2 = df.apply(lambda x: pd.Series(x['Title']),axis=1).stack().reset_index(level=1, drop=True)\n", 182 | "s2.name = 'Title'\n", 183 | "s3 = df.apply(lambda x: pd.Series(x['Keyword']),axis=1).stack().reset_index(level=1, drop=True)\n", 184 | "s3.name = 'Keyword'\n", 185 | "df_m = df.drop(['Author_Names','Title', 'Keyword'], axis=1).join(s1)\n", 186 | "df_m = df_m.join(s2)\n", 187 | "df_m = df_m.join(s3)\n", 188 | "\n", 189 | "print df_m.shapeG.nodes(data=True)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 18, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "df_m.to_csv('top_50.csv', sep=',', encoding='utf-8')" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 19, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "G=nx.from_pandas_dataframe(df_m, 'Journal', 'Author_Name', ['Title', 'Year', 'Keyword'])" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 20, 213 | "metadata": {}, 214 | "outputs": [ 215 | { 216 | "ename": "KeyboardInterrupt", 217 | "evalue": "", 218 | "output_type": "error", 219 | "traceback": [ 220 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 221 | "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", 222 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0ma_proj_sg_largest\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweighted_projected_graph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msg_largest\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAuthor_Names\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0mj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mj_proj_sg_largest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0medges\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 13\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0ma_proj_sg_largest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0medges\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 223 | "\u001b[0;32m/home/bsnacks/Py_projects/ipysigma_demo/venv/lib/python2.7/site-packages/networkx/classes/graph.pyc\u001b[0m in \u001b[0;36medges\u001b[0;34m(self, nbunch, data, default)\u001b[0m\n\u001b[1;32m 1135\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1136\u001b[0m \"\"\"\n\u001b[0;32m-> 1137\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0medges_iter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnbunch\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1138\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1139\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0medges_iter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnbunch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 224 | "\u001b[0;32m/home/bsnacks/Py_projects/ipysigma_demo/venv/lib/python2.7/site-packages/networkx/classes/graph.pyc\u001b[0m in \u001b[0;36medges_iter\u001b[0;34m(self, nbunch, data, default)\u001b[0m\n\u001b[1;32m 1196\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mnbr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mddict\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mnbrs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1197\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnbr\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mseen\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1198\u001b[0;31m \u001b[0;32myield\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnbr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mddict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1199\u001b[0m \u001b[0mseen\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1200\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mFalse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 225 | "\u001b[0;31mKeyboardInterrupt\u001b[0m: " 226 | ] 227 | } 228 | ], 229 | "source": [ 230 | "# Weighted Projections/Clustering\n", 231 | "# Largest most connected graph - 200 cut-off \n", 232 | "big_subg = [i for i in nx.connected_component_subgraphs(G) if len(i) > 200]\n", 233 | "# Largest:\n", 234 | "sg_largest = big_subg[0] # largest connected subgraph\n", 235 | "\n", 236 | "# weighted_projections applied to subgraph to separate the two components\n", 237 | "Journals,Author_Names = bi.sets(sg_largest) # split into bipartites\n", 238 | "j_proj_sg_largest = bi.weighted_projected_graph(sg_largest, Journals) \n", 239 | "a_proj_sg_largest = bi.weighted_projected_graph(sg_largest, Author_Names)\n", 240 | "\n", 241 | "j = j_proj_sg_largest.edges(data=True) \n", 242 | "a = a_proj_sg_largest.edges(data=True)" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": null, 248 | "metadata": { 249 | "collapsed": true 250 | }, 251 | "outputs": [], 252 | "source": [ 253 | "# Island Method \n", 254 | "def trim(g):\n", 255 | " g_temp = nx.Graph()\n", 256 | " edge_bunch = [i for i in g.edges(data=True)] \n", 257 | " g_temp.add_edges_from(edge_bunch)\n", 258 | " return g_temp\n", 259 | "a_sg_island = trim(a_proj_sg_largest)\n", 260 | "j_sg_island = trim(j_proj_sg_largest)\n" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": null, 266 | "metadata": {}, 267 | "outputs": [], 268 | "source": [ 269 | "#centrality\n", 270 | "a_degree = nx.degree_centrality(a_sg_island)\n", 271 | "j_degree = nx.degree_centrality(j_sg_island)" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "print a_sg_island.number_of_nodes()\n", 281 | "print j_sg_island.number_of_nodes()" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": null, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "print len(a_degree) # 12 journals\n", 291 | "a_degree" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": null, 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [ 300 | "len(j_degree) # 7268 authors" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": null, 306 | "metadata": {}, 307 | "outputs": [], 308 | "source": [ 309 | "%matplotlib inline\n", 310 | "nx.draw(a_sg_island,pos=nx.spring_layout(a_sg_island)) # use spring layout" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": null, 316 | "metadata": {}, 317 | "outputs": [], 318 | "source": [ 319 | "nx.draw(j_sg_island,pos=nx.spring_layout(j_sg_island)) # use spring layout" 320 | ] 321 | } 322 | ], 323 | "metadata": { 324 | "anaconda-cloud": {}, 325 | "kernelspec": { 326 | "display_name": "Python 2", 327 | "language": "python", 328 | "name": "python2" 329 | }, 330 | "language_info": { 331 | "codemirror_mode": { 332 | "name": "ipython", 333 | "version": 2 334 | }, 335 | "file_extension": ".py", 336 | "mimetype": "text/x-python", 337 | "name": "python", 338 | "nbconvert_exporter": "python", 339 | "pygments_lexer": "ipython2", 340 | "version": "2.7.13" 341 | } 342 | }, 343 | "nbformat": 4, 344 | "nbformat_minor": 1 345 | } 346 | -------------------------------------------------------------------------------- /IPySig_demo_data/stars_main_edges.csv: -------------------------------------------------------------------------------- 1 | id,source,target 2 | 0,0,538 3 | 1,1,538 4 | 2,2,539 5 | 3,3,539 6 | 4,4,539 7 | 5,5,539 8 | 6,5,540 9 | 7,6,539 10 | 8,6,540 11 | 9,7,539 12 | 10,7,540 13 | 11,8,540 14 | 12,9,540 15 | 13,10,540 16 | 14,11,540 17 | 15,12,540 18 | 16,13,539 19 | 17,13,540 20 | 18,14,539 21 | 19,14,540 22 | 20,15,540 23 | 21,16,540 24 | 22,17,540 25 | 23,18,540 26 | 24,19,540 27 | 25,20,540 28 | 26,21,540 29 | 27,22,540 30 | 28,23,540 31 | 29,24,539 32 | 30,24,540 33 | 31,25,538 34 | 32,26,538 35 | 33,27,541 36 | 34,28,541 37 | 35,29,541 38 | 36,30,541 39 | 37,31,541 40 | 38,32,541 41 | 39,33,541 42 | 40,34,541 43 | 41,35,541 44 | 42,36,541 45 | 43,37,541 46 | 44,38,541 47 | 45,39,541 48 | 46,40,541 49 | 47,41,541 50 | 48,42,541 51 | 49,43,541 52 | 50,44,542 53 | 51,45,542 54 | 52,46,565 55 | 53,46,543 56 | 54,47,543 57 | 55,48,544 58 | 56,49,544 59 | 57,49,553 60 | 58,50,539 61 | 59,51,539 62 | 60,52,539 63 | 61,53,541 64 | 62,54,541 65 | 63,55,541 66 | 64,56,541 67 | 65,57,541 68 | 66,58,541 69 | 67,59,541 70 | 68,60,541 71 | 69,61,539 72 | 70,62,552 73 | 71,62,539 74 | 72,63,552 75 | 73,63,539 76 | 74,63,548 77 | 75,64,538 78 | 76,65,538 79 | 77,66,545 80 | 78,66,556 81 | 79,67,546 82 | 80,68,546 83 | 81,68,555 84 | 82,69,547 85 | 83,70,547 86 | 84,71,539 87 | 85,72,548 88 | 86,73,548 89 | 87,74,546 90 | 88,75,546 91 | 89,76,549 92 | 90,77,539 93 | 91,77,540 94 | 92,77,549 95 | 93,78,546 96 | 94,79,546 97 | 95,80,546 98 | 96,81,556 99 | 97,81,550 100 | 98,82,556 101 | 99,82,550 102 | 100,83,556 103 | 101,83,550 104 | 102,84,546 105 | 103,85,546 106 | 104,86,546 107 | 105,87,546 108 | 106,88,539 109 | 107,88,551 110 | 108,89,540 111 | 109,90,549 112 | 110,91,552 113 | 111,92,552 114 | 112,93,552 115 | 113,94,552 116 | 114,95,552 117 | 115,96,553 118 | 116,96,539 119 | 117,97,541 120 | 118,98,541 121 | 119,99,546 122 | 120,100,546 123 | 121,101,546 124 | 122,102,546 125 | 123,103,546 126 | 124,104,561 127 | 125,104,546 128 | 126,105,546 129 | 127,106,546 130 | 128,107,546 131 | 129,108,553 132 | 130,109,553 133 | 131,110,546 134 | 132,111,546 135 | 133,112,546 136 | 134,113,546 137 | 135,114,546 138 | 136,115,541 139 | 137,116,554 140 | 138,117,546 141 | 139,118,546 142 | 140,119,546 143 | 141,120,539 144 | 142,121,539 145 | 143,122,549 146 | 144,123,540 147 | 145,124,540 148 | 146,125,540 149 | 147,126,540 150 | 148,127,540 151 | 149,128,540 152 | 150,129,540 153 | 151,130,555 154 | 152,131,555 155 | 153,132,555 156 | 154,133,555 157 | 155,134,549 158 | 156,135,556 159 | 157,136,546 160 | 158,137,546 161 | 159,138,546 162 | 160,139,539 163 | 161,140,557 164 | 162,141,557 165 | 163,142,551 166 | 164,143,558 167 | 165,144,539 168 | 166,145,539 169 | 167,146,539 170 | 168,146,548 171 | 169,147,539 172 | 170,148,539 173 | 171,149,539 174 | 172,150,539 175 | 173,151,539 176 | 174,152,539 177 | 175,153,539 178 | 176,153,548 179 | 177,154,539 180 | 178,154,548 181 | 179,155,539 182 | 180,156,539 183 | 181,157,539 184 | 182,158,539 185 | 183,159,557 186 | 184,160,559 187 | 185,161,569 188 | 186,161,559 189 | 187,162,560 190 | 188,163,560 191 | 189,164,560 192 | 190,165,552 193 | 191,165,548 194 | 192,166,540 195 | 193,167,553 196 | 194,168,557 197 | 195,169,557 198 | 196,170,539 199 | 197,171,539 200 | 198,172,553 201 | 199,173,553 202 | 200,174,553 203 | 201,175,539 204 | 202,176,539 205 | 203,177,539 206 | 204,178,539 207 | 205,179,546 208 | 206,180,546 209 | 207,181,548 210 | 208,182,555 211 | 209,183,561 212 | 210,184,561 213 | 211,185,561 214 | 212,186,553 215 | 213,186,539 216 | 214,187,539 217 | 215,188,539 218 | 216,189,555 219 | 217,190,555 220 | 218,191,555 221 | 219,192,541 222 | 220,193,541 223 | 221,194,539 224 | 222,195,539 225 | 223,196,553 226 | 224,197,552 227 | 225,198,552 228 | 226,199,552 229 | 227,200,552 230 | 228,201,552 231 | 229,202,552 232 | 230,203,552 233 | 231,204,552 234 | 232,205,552 235 | 233,206,552 236 | 234,207,552 237 | 235,208,552 238 | 236,209,552 239 | 237,210,552 240 | 238,211,539 241 | 239,211,540 242 | 240,212,553 243 | 241,212,539 244 | 242,212,540 245 | 243,213,539 246 | 244,213,540 247 | 245,214,539 248 | 246,214,540 249 | 247,215,539 250 | 248,215,540 251 | 249,216,539 252 | 250,216,540 253 | 251,217,539 254 | 252,217,540 255 | 253,218,539 256 | 254,218,540 257 | 255,219,539 258 | 256,219,540 259 | 257,220,553 260 | 258,220,539 261 | 259,220,540 262 | 260,221,540 263 | 261,222,539 264 | 262,223,539 265 | 263,224,539 266 | 264,225,539 267 | 265,226,539 268 | 266,227,539 269 | 267,228,539 270 | 268,229,539 271 | 269,230,539 272 | 270,231,539 273 | 271,232,539 274 | 272,233,539 275 | 273,234,539 276 | 274,235,539 277 | 275,236,539 278 | 276,237,539 279 | 277,238,539 280 | 278,239,539 281 | 279,240,553 282 | 280,240,539 283 | 281,241,539 284 | 282,242,551 285 | 283,243,551 286 | 284,244,547 287 | 285,245,547 288 | 286,246,547 289 | 287,247,547 290 | 288,248,541 291 | 289,249,541 292 | 290,250,541 293 | 291,251,541 294 | 292,252,541 295 | 293,253,541 296 | 294,254,541 297 | 295,255,541 298 | 296,256,539 299 | 297,256,548 300 | 298,256,541 301 | 299,257,562 302 | 300,258,557 303 | 301,259,557 304 | 302,260,557 305 | 303,261,539 306 | 304,262,563 307 | 305,263,539 308 | 306,264,551 309 | 307,265,551 310 | 308,266,551 311 | 309,267,539 312 | 310,267,548 313 | 311,268,548 314 | 312,269,539 315 | 313,269,541 316 | 314,270,539 317 | 315,271,561 318 | 316,272,561 319 | 317,273,556 320 | 318,274,548 321 | 319,275,548 322 | 320,276,561 323 | 321,277,561 324 | 322,278,561 325 | 323,279,561 326 | 324,280,561 327 | 325,281,548 328 | 326,282,539 329 | 327,283,564 330 | 328,284,539 331 | 329,285,539 332 | 330,286,539 333 | 331,287,539 334 | 332,288,539 335 | 333,289,539 336 | 334,290,539 337 | 335,291,539 338 | 336,292,549 339 | 337,293,549 340 | 338,294,539 341 | 339,295,539 342 | 340,296,539 343 | 341,296,548 344 | 342,297,556 345 | 343,298,546 346 | 344,299,546 347 | 345,300,546 348 | 346,301,546 349 | 347,302,546 350 | 348,303,561 351 | 349,304,561 352 | 350,305,561 353 | 351,306,548 354 | 352,307,548 355 | 353,308,548 356 | 354,309,548 357 | 355,310,548 358 | 356,311,548 359 | 357,312,548 360 | 358,313,553 361 | 359,314,553 362 | 360,315,553 363 | 361,316,540 364 | 362,317,540 365 | 363,318,540 366 | 364,319,540 367 | 365,320,540 368 | 366,321,540 369 | 367,322,542 370 | 368,323,542 371 | 369,324,542 372 | 370,325,542 373 | 371,326,561 374 | 372,327,540 375 | 373,328,540 376 | 374,329,566 377 | 375,330,566 378 | 376,331,539 379 | 377,332,539 380 | 378,333,567 381 | 379,334,567 382 | 380,335,567 383 | 381,336,542 384 | 382,337,542 385 | 383,338,555 386 | 384,339,555 387 | 385,340,555 388 | 386,341,555 389 | 387,342,555 390 | 388,343,555 391 | 389,344,555 392 | 390,345,553 393 | 391,345,539 394 | 392,346,553 395 | 393,347,552 396 | 394,348,552 397 | 395,349,556 398 | 396,350,546 399 | 397,351,542 400 | 398,352,542 401 | 399,353,542 402 | 400,354,542 403 | 401,355,542 404 | 402,356,542 405 | 403,357,542 406 | 404,358,542 407 | 405,359,542 408 | 406,360,542 409 | 407,361,542 410 | 408,362,542 411 | 409,363,542 412 | 410,364,542 413 | 411,365,542 414 | 412,366,542 415 | 413,367,542 416 | 414,368,542 417 | 415,369,542 418 | 416,370,542 419 | 417,371,539 420 | 418,372,539 421 | 419,373,539 422 | 420,374,539 423 | 421,375,539 424 | 422,376,539 425 | 423,377,539 426 | 424,378,561 427 | 425,379,568 428 | 426,380,568 429 | 427,381,568 430 | 428,382,568 431 | 429,383,568 432 | 430,384,539 433 | 431,385,557 434 | 432,386,549 435 | 433,387,539 436 | 434,388,546 437 | 435,389,546 438 | 436,390,546 439 | 437,391,546 440 | 438,392,538 441 | 439,393,538 442 | 440,394,538 443 | 441,395,540 444 | 442,396,570 445 | 443,397,570 446 | 444,398,539 447 | 445,399,539 448 | 446,400,539 449 | 447,401,539 450 | 448,402,541 451 | 449,403,541 452 | 450,404,541 453 | 451,405,541 454 | 452,406,541 455 | 453,407,541 456 | 454,408,541 457 | 455,409,541 458 | 456,410,541 459 | 457,411,541 460 | 458,412,541 461 | 459,413,541 462 | 460,414,539 463 | 461,414,541 464 | 462,415,541 465 | 463,416,541 466 | 464,417,541 467 | 465,418,541 468 | 466,419,541 469 | 467,420,541 470 | 468,421,541 471 | 469,422,541 472 | 470,423,541 473 | 471,424,541 474 | 472,425,541 475 | 473,426,539 476 | 474,427,539 477 | 475,428,539 478 | 476,429,539 479 | 477,430,571 480 | 478,431,571 481 | 479,432,571 482 | 480,433,571 483 | 481,434,571 484 | 482,435,541 485 | 483,436,541 486 | 484,437,541 487 | 485,438,547 488 | 486,439,547 489 | 487,440,547 490 | 488,441,547 491 | 489,442,553 492 | 490,443,539 493 | 491,444,539 494 | 492,444,548 495 | 493,445,539 496 | 494,445,548 497 | 495,446,539 498 | 496,446,548 499 | 497,447,539 500 | 498,448,539 501 | 499,449,539 502 | 500,450,539 503 | 501,450,548 504 | 502,451,539 505 | 503,451,548 506 | 504,452,546 507 | 505,453,548 508 | 506,454,548 509 | 507,455,548 510 | 508,456,539 511 | 509,457,539 512 | 510,457,548 513 | 511,458,539 514 | 512,459,539 515 | 513,459,549 516 | 514,460,539 517 | 515,461,556 518 | 516,462,551 519 | 517,463,551 520 | 518,464,551 521 | 519,465,551 522 | 520,466,551 523 | 521,467,551 524 | 522,468,551 525 | 523,469,551 526 | 524,470,539 527 | 525,471,539 528 | 526,472,539 529 | 527,473,539 530 | 528,474,539 531 | 529,475,548 532 | 530,476,548 533 | 531,477,572 534 | 532,478,572 535 | 533,479,572 536 | 534,480,542 537 | 535,481,542 538 | 536,482,542 539 | 537,483,542 540 | 538,484,542 541 | 539,485,542 542 | 540,486,573 543 | 541,487,573 544 | 542,488,573 545 | 543,489,573 546 | 544,490,573 547 | 545,491,573 548 | 546,492,573 549 | 547,493,573 550 | 548,494,573 551 | 549,495,573 552 | 550,496,573 553 | 551,497,573 554 | 552,498,573 555 | 553,499,542 556 | 554,500,542 557 | 555,501,542 558 | 556,502,542 559 | 557,503,542 560 | 558,504,542 561 | 559,505,542 562 | 560,506,542 563 | 561,507,542 564 | 562,508,542 565 | 563,509,542 566 | 564,510,542 567 | 565,511,542 568 | 566,512,539 569 | 567,513,539 570 | 568,514,539 571 | 569,515,539 572 | 570,516,572 573 | 571,517,572 574 | 572,518,574 575 | 573,519,574 576 | 574,520,574 577 | 575,521,574 578 | 576,522,541 579 | 577,523,541 580 | 578,524,548 581 | 579,525,548 582 | 580,526,548 583 | 581,527,548 584 | 582,528,548 585 | 583,529,548 586 | 584,530,541 587 | 585,531,548 588 | 586,532,553 589 | 587,532,548 590 | 588,533,555 591 | 589,534,575 592 | 590,535,556 593 | 591,536,556 594 | 592,537,556 595 | -------------------------------------------------------------------------------- /IPySig_demo_data/stars_main_nodes.csv: -------------------------------------------------------------------------------- 1 | id,label,node_type,zbetween_central,zdeg_central,zpagerank 2 | 0,"Monkhorst, Hendrik J.",Author,0.0,0.00173913043478,0.00102727844508 3 | 1,"Pack, James D.",Author,0.0,0.00173913043478,0.00102727844508 4 | 2,"Schlegel, David J.",Author,0.0,0.00173913043478,0.000848161729492 5 | 3,"Finkbeiner, Douglas P.",Author,0.0,0.00173913043478,0.000848161729492 6 | 4,"Davis, Marc",Author,0.0,0.00173913043478,0.000848161729492 7 | 5,"Riess, Adam G.",Author,0.00310587434928,0.00347826086957,0.00142598618231 8 | 6,"Filippenko, Alexei V.",Author,0.00310587434928,0.00347826086957,0.00142598618231 9 | 7,"Challis, Peter",Author,0.00310587434928,0.00347826086957,0.00142598618231 10 | 8,"Clocchiatti, Alejandro",Author,0.0,0.00173913043478,0.000838241119484 11 | 9,"Diercks, Alan",Author,0.0,0.00173913043478,0.000838241119484 12 | 10,"Garnavich, Peter M.",Author,0.0,0.00173913043478,0.000838241119484 13 | 11,"Gilliland, Ron L.",Author,0.0,0.00173913043478,0.000838241119484 14 | 12,"Hogan, Craig J.",Author,0.0,0.00173913043478,0.000838241119484 15 | 13,"Jha, Saurabh",Author,0.00310587434928,0.00347826086957,0.00142598618231 16 | 14,"Kirshner, Robert P.",Author,0.00310587434928,0.00347826086957,0.00142598618231 17 | 15,"Leibundgut, B.",Author,0.0,0.00173913043478,0.000838241119484 18 | 16,"Phillips, M. M.",Author,0.0,0.00173913043478,0.000838241119484 19 | 17,"Reiss, David",Author,0.0,0.00173913043478,0.000838241119484 20 | 18,"Schmidt, Brian P.",Author,0.0,0.00173913043478,0.000838241119484 21 | 19,"Schommer, Robert A.",Author,0.0,0.00173913043478,0.000838241119484 22 | 20,"Smith, R. Chris",Author,0.0,0.00173913043478,0.000838241119484 23 | 21,"Spyromilio, J.",Author,0.0,0.00173913043478,0.000838241119484 24 | 22,"Stubbs, Christopher",Author,0.0,0.00173913043478,0.000838241119484 25 | 23,"Suntzeff, Nicholas B.",Author,0.0,0.00173913043478,0.000838241119484 26 | 24,"Tonry, John",Author,0.00310587434928,0.00347826086957,0.00142598618231 27 | 25,"Kresse, G.",Author,0.0,0.00173913043478,0.00102727844508 28 | 26,"Hafner, J.",Author,0.0,0.00173913043478,0.00102727844508 29 | 27,"Spergel, D. N.",Author,0.0,0.00173913043478,0.000928786654459 30 | 28,"Verde, L.",Author,0.0,0.00173913043478,0.000928786654459 31 | 29,"Peiris, H. V.",Author,0.0,0.00173913043478,0.000928786654459 32 | 30,"Komatsu, E.",Author,0.0,0.00173913043478,0.000928786654459 33 | 31,"Nolta, M. R.",Author,0.0,0.00173913043478,0.000928786654459 34 | 32,"Bennett, C. L.",Author,0.0,0.00173913043478,0.000928786654459 35 | 33,"Halpern, M.",Author,0.0,0.00173913043478,0.000928786654459 36 | 34,"Hinshaw, G.",Author,0.0,0.00173913043478,0.000928786654459 37 | 35,"Jarosik, N.",Author,0.0,0.00173913043478,0.000928786654459 38 | 36,"Kogut, A.",Author,0.0,0.00173913043478,0.000928786654459 39 | 37,"Limon, M.",Author,0.0,0.00173913043478,0.000928786654459 40 | 38,"Meyer, S. S.",Author,0.0,0.00173913043478,0.000928786654459 41 | 39,"Page, L.",Author,0.0,0.00173913043478,0.000928786654459 42 | 40,"Tucker, G. S.",Author,0.0,0.00173913043478,0.000928786654459 43 | 41,"Weiland, J. L.",Author,0.0,0.00173913043478,0.000928786654459 44 | 42,"Wollack, E.",Author,0.0,0.00173913043478,0.000928786654459 45 | 43,"Wright, E. L.",Author,0.0,0.00173913043478,0.000928786654459 46 | 44,"Shakura, N. I.",Author,0.0,0.00173913043478,0.000955641101175 47 | 45,"Sunyaev, R. A.",Author,0.0,0.00173913043478,0.000955641101175 48 | 46,"Geim, A. K.",Author,1.21193758521e-05,0.00347826086957,0.00225389863548 49 | 47,"Novoselov, K. S.",Author,0.0,0.00173913043478,0.00121832358674 50 | 48,"Anders, Edward",Author,0.0,0.00173913043478,0.00104048099268 51 | 49,"Grevesse, Nicolas",Author,0.00399939403121,0.00347826086957,0.00162469168036 52 | 50,"Cardelli, Jason A.",Author,0.0,0.00173913043478,0.000848161729492 53 | 51,"Clayton, Geoffrey C.",Author,0.0,0.00173913043478,0.000848161729492 54 | 52,"Mathis, John S.",Author,0.0,0.00173913043478,0.000848161729492 55 | 53,"Bean, R.",Author,0.0,0.00173913043478,0.000928786654459 56 | 54,"Doré, O.",Author,0.0,0.00173913043478,0.000928786654459 57 | 55,"Dunkley, J.",Author,0.0,0.00173913043478,0.000928786654459 58 | 56,"Hill, R. S.",Author,0.0,0.00173913043478,0.000928786654459 59 | 57,"Odegard, N.",Author,0.0,0.00173913043478,0.000928786654459 60 | 58,"Smith, K. M.",Author,0.0,0.00173913043478,0.000928786654459 61 | 59,"Gold, B.",Author,0.0,0.00173913043478,0.000928786654459 62 | 60,"Larson, D.",Author,0.0,0.00173913043478,0.000928786654459 63 | 61,"Navarro, Julio F.",Author,0.0,0.00173913043478,0.000848161729492 64 | 62,"Frenk, Carlos S.",Author,0.017052403192,0.00347826086957,0.00150005334866 65 | 63,"White, Simon D. M.",Author,0.0230506758409,0.00521739130435,0.00207803322717 66 | 64,"Johnson, P. B.",Author,0.0,0.00173913043478,0.00102727844508 67 | 65,"Christy, R. W.",Author,0.0,0.00173913043478,0.00102727844508 68 | 66,"Hawking, S. W.",Author,7.87759430389e-05,0.00347826086957,0.00176955169987 69 | 67,"Hasan, M. Z.",Author,0.0,0.00173913043478,0.000942684553686 70 | 68,"Kane, C. L.",Author,0.00618088168459,0.00347826086957,0.00164627294566 71 | 69,"Bertin, E.",Author,0.0,0.00173913043478,0.00101841803676 72 | 70,"Arnouts, S.",Author,0.0,0.00173913043478,0.00101841803676 73 | 71,"Salpeter, Edwin E.",Author,0.0,0.00173913043478,0.000848161729492 74 | 72,"Bruzual, G.",Author,0.0,0.00173913043478,0.000838396545174 75 | 73,"Charlot, S.",Author,0.0,0.00173913043478,0.000838396545174 76 | 74,"Albert, Réka",Author,0.0,0.00173913043478,0.000942684553686 77 | 75,"Barabási, Albert-László",Author,0.0,0.00173913043478,0.000942684553686 78 | 76,"Binney, James",Author,0.0,0.00173913043478,0.000915640751368 79 | 77,"Tremaine, Scott",Author,0.011956442926,0.00521739130435,0.00208121026701 80 | 78,"Žutić, Igor",Author,0.0,0.00173913043478,0.000942684553686 81 | 79,"Fabian, Jaroslav",Author,0.0,0.00173913043478,0.000942684553686 82 | 80,"Das Sarma, S.",Author,0.0,0.00173913043478,0.000942684553686 83 | 81,"Arkani-Hamed, Nima",Author,2.22188557289e-05,0.00347826086957,0.00153852321542 84 | 82,"Dimopoulos, Savas",Author,2.22188557289e-05,0.00347826086957,0.00153852321542 85 | 83,"Dvali, Gia",Author,2.22188557289e-05,0.00347826086957,0.00153852321542 86 | 84,"Ando, Tsuneya",Author,0.0,0.00173913043478,0.000942684553686 87 | 85,"Fowler, Alan B.",Author,0.0,0.00173913043478,0.000942684553686 88 | 86,"Stern, Frank",Author,0.0,0.00173913043478,0.000942684553686 89 | 87,"Chandrasekhar, S.",Author,0.0,0.00173913043478,0.000942684553686 90 | 88,"Stetson, Peter B.",Author,0.0288138160885,0.00347826086957,0.00154729908598 91 | 89,"Landolt, Arlo U.",Author,0.0,0.00173913043478,0.000838241119484 92 | 90,"Peebles, Phillip James Edwin",Author,0.0,0.00173913043478,0.000915640751368 93 | 91,"Kroto, H. W.",Author,0.0,0.00173913043478,0.000912308285833 94 | 92,"Heath, J. R.",Author,0.0,0.00173913043478,0.000912308285833 95 | 93,"O'Brien, S. C.",Author,0.0,0.00173913043478,0.000912308285833 96 | 94,"Curl, R. F.",Author,0.0,0.00173913043478,0.000912308285833 97 | 95,"Smalley, R. E.",Author,0.0,0.00173913043478,0.000912308285833 98 | 96,"Kennicutt, Robert C., Jr.",Author,0.00383537999673,0.00347826086957,0.00143237241717 99 | 97,"Barnes, C.",Author,0.0,0.00173913043478,0.000928786654459 100 | 98,"Greason, M. R.",Author,0.0,0.00173913043478,0.000928786654459 101 | 99,"Qi, Xiao-Liang",Author,0.0,0.00173913043478,0.000942684553686 102 | 100,"Zhang, Shou-Cheng",Author,0.0,0.00173913043478,0.000942684553686 103 | 101,"Bloch, Immanuel",Author,0.0,0.00173913043478,0.000942684553686 104 | 102,"Dalibard, Jean",Author,0.0,0.00173913043478,0.000942684553686 105 | 103,"Zwerger, Wilhelm",Author,0.0,0.00173913043478,0.000942684553686 106 | 104,"Georges, Antoine",Author,0.00591425541585,0.00347826086957,0.00164773753447 107 | 105,"Kotliar, Gabriel",Author,0.0,0.00173913043478,0.000942684553686 108 | 106,"Krauth, Werner",Author,0.0,0.00173913043478,0.000942684553686 109 | 107,"Rozenberg, Marcelo J.",Author,0.0,0.00173913043478,0.000942684553686 110 | 108,"Dickey, J. M.",Author,0.0,0.00173913043478,0.000844627354347 111 | 109,"Lockman, F. J.",Author,0.0,0.00173913043478,0.000844627354347 112 | 110,"Payne, M. C.",Author,0.0,0.00173913043478,0.000942684553686 113 | 111,"Teter, M. P.",Author,0.0,0.00173913043478,0.000942684553686 114 | 112,"Allan, D. C.",Author,0.0,0.00173913043478,0.000942684553686 115 | 113,"Arias, T. A.",Author,0.0,0.00173913043478,0.000942684553686 116 | 114,"Joannopoulos, J. D.",Author,0.0,0.00173913043478,0.000942684553686 117 | 115,"Kurucz, R. L.",Author,0.0,0.00173913043478,0.000928786654459 118 | 116,"Rodríguez-Carvajal, Juan",Author,0.0,0.00173913043478,0.00173611111111 119 | 117,"Imada, Masatoshi",Author,0.0,0.00173913043478,0.000942684553686 120 | 118,"Fujimori, Atsushi",Author,0.0,0.00173913043478,0.000942684553686 121 | 119,"Tokura, Yoshinori",Author,0.0,0.00173913043478,0.000942684553686 122 | 120,"Press, William H.",Author,0.0,0.00173913043478,0.000848161729492 123 | 121,"Schechter, Paul",Author,0.0,0.00173913043478,0.000848161729492 124 | 122,"Osterbrock, Donald E.",Author,0.0,0.00173913043478,0.000915640751368 125 | 123,"Condon, J. J.",Author,0.0,0.00173913043478,0.000838241119484 126 | 124,"Cotton, W. D.",Author,0.0,0.00173913043478,0.000838241119484 127 | 125,"Greisen, E. W.",Author,0.0,0.00173913043478,0.000838241119484 128 | 126,"Yin, Q. F.",Author,0.0,0.00173913043478,0.000838241119484 129 | 127,"Perley, R. A.",Author,0.0,0.00173913043478,0.000838241119484 130 | 128,"Taylor, G. B.",Author,0.0,0.00173913043478,0.000838241119484 131 | 129,"Broderick, J. J.",Author,0.0,0.00173913043478,0.000838241119484 132 | 130,"Laughlin, R. B.",Author,0.0,0.00173913043478,0.000964005058639 133 | 131,"Bak, Per",Author,0.0,0.00173913043478,0.000964005058639 134 | 132,"Tang, Chao",Author,0.0,0.00173913043478,0.000964005058639 135 | 133,"Wiesenfeld, Kurt",Author,0.0,0.00173913043478,0.000964005058639 136 | 134,"Spitzer, Lyman",Author,0.0,0.00173913043478,0.000915640751368 137 | 135,"Bekenstein, Jacob D.",Author,0.0,0.00173913043478,0.000908910155458 138 | 136,"Weinberg, Steven",Author,0.0,0.00173913043478,0.000942684553686 139 | 137,"Binder, K.",Author,0.0,0.00173913043478,0.000942684553686 140 | 138,"Young, A. P.",Author,0.0,0.00173913043478,0.000942684553686 141 | 139,"Scargle, J. D.",Author,0.0,0.00173913043478,0.000848161729492 142 | 140,"Brans, C.",Author,0.0,0.00173913043478,0.00102727844508 143 | 141,"Dicke, R. H.",Author,0.0,0.00173913043478,0.00102727844508 144 | 142,"Chabrier, Gilles",Author,0.0,0.00173913043478,0.00095955402315 145 | 143,"Arnaud, K. A.",Author,0.0,0.00173913043478,0.00173611111111 146 | 144,"Strolger, Louis-Gregory",Author,0.0,0.00173913043478,0.000848161729492 147 | 145,"Casertano, Stefano",Author,0.0,0.00173913043478,0.000848161729492 148 | 146,"Ferguson, Henry C.",Author,0.00243523385967,0.00347826086957,0.001426141608 149 | 147,"Mobasher, Bahram",Author,0.0,0.00173913043478,0.000848161729492 150 | 148,"Li, Weidong",Author,0.0,0.00173913043478,0.000848161729492 151 | 149,"Chornock, Ryan",Author,0.0,0.00173913043478,0.000848161729492 152 | 150,"Leibundgut, Bruno",Author,0.0,0.00173913043478,0.000848161729492 153 | 151,"Dickinson, Mark",Author,0.0,0.00173913043478,0.000848161729492 154 | 152,"Livio, Mario",Author,0.0,0.00173913043478,0.000848161729492 155 | 153,"Giavalisco, Mauro",Author,0.00243523385967,0.00347826086957,0.001426141608 156 | 154,"Steidel, Charles C.",Author,0.00243523385967,0.00347826086957,0.001426141608 157 | 155,"Benítez, Txitxo",Author,0.0,0.00173913043478,0.000848161729492 158 | 156,"Tsvetanov, Zlatan",Author,0.0,0.00173913043478,0.000848161729492 159 | 157,"Draine, B. T.",Author,0.0,0.00173913043478,0.000848161729492 160 | 158,"Lee, H. M.",Author,0.0,0.00173913043478,0.000848161729492 161 | 159,"Onsager, Lars",Author,0.0,0.00173913043478,0.00102727844508 162 | 160,"Olive, K. A.",Author,0.0,0.00173913043478,0.00121832358674 163 | 161,Particle Data Group,Author,1.21193758521e-05,0.00347826086957,0.00225389863548 164 | 162,"Copeland, Edmund J.",Author,0.0,0.00173913043478,0.00120448661159 165 | 163,"Sami, M.",Author,0.0,0.00173913043478,0.00120448661159 166 | 164,"Tsujikawa, Shinji",Author,0.0,0.00173913043478,0.00120448661159 167 | 165,"Springel, Volker",Author,0.00356303878922,0.00347826086957,0.00149028816434 168 | 166,"Harris, William E.",Author,0.0,0.00173913043478,0.000838241119484 169 | 167,"Johnson, Harold L.",Author,0.0,0.00173913043478,0.000844627354347 170 | 168,"Slater, J. C.",Author,0.0,0.00173913043478,0.00102727844508 171 | 169,"Koster, G. F.",Author,0.0,0.00173913043478,0.00102727844508 172 | 170,"Ferrarese, Laura",Author,0.0,0.00173913043478,0.000848161729492 173 | 171,"Merritt, David",Author,0.0,0.00173913043478,0.000848161729492 174 | 172,"Asplund, Martin",Author,0.0,0.00173913043478,0.000844627354347 175 | 173,"Sauval, A. Jacques",Author,0.0,0.00173913043478,0.000844627354347 176 | 174,"Scott, Pat",Author,0.0,0.00173913043478,0.000844627354347 177 | 175,"Bardeen, J. M.",Author,0.0,0.00173913043478,0.000848161729492 178 | 176,"Bond, J. R.",Author,0.0,0.00173913043478,0.000848161729492 179 | 177,"Kaiser, N.",Author,0.0,0.00173913043478,0.000848161729492 180 | 178,"Szalay, A. S.",Author,0.0,0.00173913043478,0.000848161729492 181 | 179,"Peebles, P. J.",Author,0.0,0.00173913043478,0.000942684553686 182 | 180,"Ratra, Bharat",Author,0.0,0.00173913043478,0.000942684553686 183 | 181,"Kroupa, Pavel",Author,0.0,0.00173913043478,0.000838396545174 184 | 182,"Mele, E. J.",Author,0.0,0.00173913043478,0.000964005058639 185 | 183,"Jungman, G.",Author,0.0,0.00173913043478,0.000965469647454 186 | 184,"Kamionkowski, M.",Author,0.0,0.00173913043478,0.000965469647454 187 | 185,"Griest, K.",Author,0.0,0.00173913043478,0.000965469647454 188 | 186,"Mathis, J. S.",Author,0.00383537999673,0.00347826086957,0.00143237241717 189 | 187,"Rumpl, W.",Author,0.0,0.00173913043478,0.000848161729492 190 | 188,"Nordsieck, K. H.",Author,0.0,0.00173913043478,0.000848161729492 191 | 189,"Klitzing, K. V.",Author,0.0,0.00173913043478,0.000964005058639 192 | 190,"Dorda, G.",Author,0.0,0.00173913043478,0.000964005058639 193 | 191,"Pepper, M.",Author,0.0,0.00173913043478,0.000964005058639 194 | 192,"Woosley, S. E.",Author,0.0,0.00173913043478,0.000928786654459 195 | 193,"Weaver, Thomas A.",Author,0.0,0.00173913043478,0.000928786654459 196 | 194,"Balbus, Steven A.",Author,0.0,0.00173913043478,0.000848161729492 197 | 195,"Hawley, John F.",Author,0.0,0.00173913043478,0.000848161729492 198 | 196,"Antonucci, R.",Author,0.0,0.00173913043478,0.000844627354347 199 | 197,"Jenkins, Adrian",Author,0.0,0.00173913043478,0.000912308285833 200 | 198,"Yoshida, Naoki",Author,0.0,0.00173913043478,0.000912308285833 201 | 199,"Gao, Liang",Author,0.0,0.00173913043478,0.000912308285833 202 | 200,"Navarro, Julio",Author,0.0,0.00173913043478,0.000912308285833 203 | 201,"Thacker, Robert",Author,0.0,0.00173913043478,0.000912308285833 204 | 202,"Croton, Darren",Author,0.0,0.00173913043478,0.000912308285833 205 | 203,"Helly, John",Author,0.0,0.00173913043478,0.000912308285833 206 | 204,"Peacock, John A.",Author,0.0,0.00173913043478,0.000912308285833 207 | 205,"Cole, Shaun",Author,0.0,0.00173913043478,0.000912308285833 208 | 206,"Thomas, Peter",Author,0.0,0.00173913043478,0.000912308285833 209 | 207,"Couchman, Hugh",Author,0.0,0.00173913043478,0.000912308285833 210 | 208,"Evrard, August",Author,0.0,0.00173913043478,0.000912308285833 211 | 209,"Colberg, Jörg",Author,0.0,0.00173913043478,0.000912308285833 212 | 210,"Pearce, Frazer",Author,0.0,0.00173913043478,0.000912308285833 213 | 211,"Magorrian, John",Author,0.00310587434928,0.00347826086957,0.00142598618231 214 | 212,"Richstone, Douglas",Author,0.00900435948306,0.00521739130435,0.00201019686999 215 | 213,"Bender, Ralf",Author,0.00310587434928,0.00347826086957,0.00142598618231 216 | 214,"Bower, Gary",Author,0.00310587434928,0.00347826086957,0.00142598618231 217 | 215,"Dressler, Alan",Author,0.00310587434928,0.00347826086957,0.00142598618231 218 | 216,"Faber, S. M.",Author,0.00310587434928,0.00347826086957,0.00142598618231 219 | 217,"Gebhardt, Karl",Author,0.00310587434928,0.00347826086957,0.00142598618231 220 | 218,"Green, Richard",Author,0.00310587434928,0.00347826086957,0.00142598618231 221 | 219,"Grillmair, Carl",Author,0.00310587434928,0.00347826086957,0.00142598618231 222 | 220,"Kormendy, John",Author,0.00900435948306,0.00521739130435,0.00201019686999 223 | 221,"Lauer, Tod",Author,0.0,0.00173913043478,0.000838241119484 224 | 222,"Freedman, Wendy L.",Author,0.0,0.00173913043478,0.000848161729492 225 | 223,"Madore, Barry F.",Author,0.0,0.00173913043478,0.000848161729492 226 | 224,"Gibson, Brad K.",Author,0.0,0.00173913043478,0.000848161729492 227 | 225,"Kelson, Daniel D.",Author,0.0,0.00173913043478,0.000848161729492 228 | 226,"Sakai, Shoko",Author,0.0,0.00173913043478,0.000848161729492 229 | 227,"Mould, Jeremy R.",Author,0.0,0.00173913043478,0.000848161729492 230 | 228,"Ford, Holland C.",Author,0.0,0.00173913043478,0.000848161729492 231 | 229,"Graham, John A.",Author,0.0,0.00173913043478,0.000848161729492 232 | 230,"Huchra, John P.",Author,0.0,0.00173913043478,0.000848161729492 233 | 231,"Hughes, Shaun M. G.",Author,0.0,0.00173913043478,0.000848161729492 234 | 232,"Illingworth, Garth D.",Author,0.0,0.00173913043478,0.000848161729492 235 | 233,"Macri, Lucas M.",Author,0.0,0.00173913043478,0.000848161729492 236 | 234,"Ho, Luis C.",Author,0.0,0.00173913043478,0.000848161729492 237 | 235,"Lauer, Tod R.",Author,0.0,0.00173913043478,0.000848161729492 238 | 236,"Pinkney, Jason",Author,0.0,0.00173913043478,0.000848161729492 239 | 237,"Gunn, James E.",Author,0.0,0.00173913043478,0.000848161729492 240 | 238,"Gott, J. Richard, III",Author,0.0,0.00173913043478,0.000848161729492 241 | 239,"Bohlin, R. C.",Author,0.0,0.00173913043478,0.000848161729492 242 | 240,"Savage, B. D.",Author,0.00383537999673,0.00347826086957,0.00143237241717 243 | 241,"Drake, J. F.",Author,0.0,0.00173913043478,0.000848161729492 244 | 242,"Urry, C. Megan",Author,0.0,0.00173913043478,0.00095955402315 245 | 243,"Padovani, Paolo",Author,0.0,0.00173913043478,0.00095955402315 246 | 244,"Schaller, G.",Author,0.0,0.00173913043478,0.00101841803676 247 | 245,"Schaerer, D.",Author,0.0,0.00173913043478,0.00101841803676 248 | 246,"Meynet, G.",Author,0.0,0.00173913043478,0.00101841803676 249 | 247,"Maeder, A.",Author,0.0,0.00173913043478,0.00101841803676 250 | 248,"Leitherer, Claus",Author,0.0,0.00173913043478,0.000928786654459 251 | 249,"Schaerer, Daniel",Author,0.0,0.00173913043478,0.000928786654459 252 | 250,"Goldader, Jeffrey D.",Author,0.0,0.00173913043478,0.000928786654459 253 | 251,"Delgado, Rosa M. González",Author,0.0,0.00173913043478,0.000928786654459 254 | 252,"Robert, Carmelle",Author,0.0,0.00173913043478,0.000928786654459 255 | 253,"Kune, Denis Foo",Author,0.0,0.00173913043478,0.000928786654459 256 | 254,"de Mello, Duília F.",Author,0.0,0.00173913043478,0.000928786654459 257 | 255,"Devost, Daniel",Author,0.0,0.00173913043478,0.000928786654459 258 | 256,"Heckman, Timothy M.",Author,0.0489792377871,0.00521739130435,0.00209451159579 259 | 257,"Berenger, Jean-Pierre",Author,0.0,0.00173913043478,0.00173611111111 260 | 258,"Bhatnagar, P. L.",Author,0.0,0.00173913043478,0.00102727844508 261 | 259,"Gross, E. P.",Author,0.0,0.00173913043478,0.00102727844508 262 | 260,"Krook, M.",Author,0.0,0.00173913043478,0.00102727844508 263 | 261,"Dressler, A.",Author,0.0,0.00173913043478,0.000848161729492 264 | 262,ESA,Author,0.0,0.00173913043478,0.00173611111111 265 | 263,"Schechter, P.",Author,0.0,0.00173913043478,0.000848161729492 266 | 264,"Baldwin, J. A.",Author,0.0,0.00173913043478,0.00095955402315 267 | 265,"Phillips, M.",Author,0.0,0.00173913043478,0.00095955402315 268 | 266,"Terlevich, R.",Author,0.0,0.00173913043478,0.00095955402315 269 | 267,"White, S. D. M.",Author,0.00243523385967,0.00347826086957,0.001426141608 270 | 268,"Rees, M. J.",Author,0.0,0.00173913043478,0.000838396545174 271 | 269,"Rieke, G. H.",Author,0.0307200663376,0.00347826086957,0.00151653171728 272 | 270,"Lebofsky, M. J.",Author,0.0,0.00173913043478,0.000848161729492 273 | 271,"Metzler, Ralf",Author,0.0,0.00173913043478,0.000965469647454 274 | 272,"Klafter, Joseph",Author,0.0,0.00173913043478,0.000965469647454 275 | 273,"Unruh, W. G.",Author,0.0,0.00173913043478,0.000908910155458 276 | 274,"Blandford, R. D.",Author,0.0,0.00173913043478,0.000838396545174 277 | 275,"Payne, D. G.",Author,0.0,0.00173913043478,0.000838396545174 278 | 276,"Boccaletti, S.",Author,0.0,0.00173913043478,0.000965469647454 279 | 277,"Latora, V.",Author,0.0,0.00173913043478,0.000965469647454 280 | 278,"Moreno, Y.",Author,0.0,0.00173913043478,0.000965469647454 281 | 279,"Chavez, M.",Author,0.0,0.00173913043478,0.000965469647454 282 | 280,"Hwang, D. -U.",Author,0.0,0.00173913043478,0.000965469647454 283 | 281,"Znajek, R. L.",Author,0.0,0.00173913043478,0.000838396545174 284 | 282,"Toomre, A.",Author,0.0,0.00173913043478,0.000848161729492 285 | 283,"Kitaev, A. Yu.",Author,0.0,0.00173913043478,0.00173611111111 286 | 284,"Toomre, Alar",Author,0.0,0.00173913043478,0.000848161729492 287 | 285,"Toomre, Juri",Author,0.0,0.00173913043478,0.000848161729492 288 | 286,"Calzetti, Daniela",Author,0.0,0.00173913043478,0.000848161729492 289 | 287,"Armus, Lee",Author,0.0,0.00173913043478,0.000848161729492 290 | 288,"Bohlin, Ralph C.",Author,0.0,0.00173913043478,0.000848161729492 291 | 289,"Kinney, Anne L.",Author,0.0,0.00173913043478,0.000848161729492 292 | 290,"Koornneef, Jan",Author,0.0,0.00173913043478,0.000848161729492 293 | 291,"Storchi-Bergmann, Thaisa",Author,0.0,0.00173913043478,0.000848161729492 294 | 292,"Shapiro, Stuart Louis",Author,0.0,0.00173913043478,0.000915640751368 295 | 293,"Teukolsky, Saul Arno",Author,0.0,0.00173913043478,0.000915640751368 296 | 294,"Davis, M.",Author,0.0,0.00173913043478,0.000848161729492 297 | 295,"Efstathiou, G.",Author,0.0,0.00173913043478,0.000848161729492 298 | 296,"Frenk, C. S.",Author,0.00243523385967,0.00347826086957,0.001426141608 299 | 297,"Bjorken, J. D.",Author,0.0,0.00173913043478,0.000908910155458 300 | 298,"Nayak, Chetan",Author,0.0,0.00173913043478,0.000942684553686 301 | 299,"Simon, Steven H.",Author,0.0,0.00173913043478,0.000942684553686 302 | 300,"Stern, Ady",Author,0.0,0.00173913043478,0.000942684553686 303 | 301,"Freedman, Michael",Author,0.0,0.00173913043478,0.000942684553686 304 | 302,"Das Sarma, Sankar",Author,0.0,0.00173913043478,0.000942684553686 305 | 303,"Bertone, Gianfranco",Author,0.0,0.00173913043478,0.000965469647454 306 | 304,"Hooper, Dan",Author,0.0,0.00173913043478,0.000965469647454 307 | 305,"Silk, Joseph",Author,0.0,0.00173913043478,0.000965469647454 308 | 306,"Croton, Darren J.",Author,0.0,0.00173913043478,0.000838396545174 309 | 307,"De Lucia, G.",Author,0.0,0.00173913043478,0.000838396545174 310 | 308,"Gao, L.",Author,0.0,0.00173913043478,0.000838396545174 311 | 309,"Jenkins, A.",Author,0.0,0.00173913043478,0.000838396545174 312 | 310,"Kauffmann, G.",Author,0.0,0.00173913043478,0.000838396545174 313 | 311,"Navarro, J. F.",Author,0.0,0.00173913043478,0.000838396545174 314 | 312,"Yoshida, N.",Author,0.0,0.00173913043478,0.000838396545174 315 | 313,"Shu, F. H.",Author,0.0,0.00173913043478,0.000844627354347 316 | 314,"Adams, F. C.",Author,0.0,0.00173913043478,0.000844627354347 317 | 315,"Lizano, S.",Author,0.0,0.00173913043478,0.000844627354347 318 | 316,"Fukugita, M.",Author,0.0,0.00173913043478,0.000838241119484 319 | 317,"Ichikawa, T.",Author,0.0,0.00173913043478,0.000838241119484 320 | 318,"Gunn, J. E.",Author,0.0,0.00173913043478,0.000838241119484 321 | 319,"Doi, M.",Author,0.0,0.00173913043478,0.000838241119484 322 | 320,"Shimasaku, K.",Author,0.0,0.00173913043478,0.000838241119484 323 | 321,"Schneider, D. P.",Author,0.0,0.00173913043478,0.000838241119484 324 | 322,"Baraffe, I.",Author,0.0,0.00173913043478,0.000955641101175 325 | 323,"Chabrier, G.",Author,0.0,0.00173913043478,0.000955641101175 326 | 324,"Allard, F.",Author,0.0,0.00173913043478,0.000955641101175 327 | 325,"Hauschildt, P. H.",Author,0.0,0.00173913043478,0.000955641101175 328 | 326,"Padmanabhan, T.",Author,0.0,0.00173913043478,0.000965469647454 329 | 327,"Burstein, D.",Author,0.0,0.00173913043478,0.000838241119484 330 | 328,"Heiles, C.",Author,0.0,0.00173913043478,0.000838241119484 331 | 329,"Stacey, J. S.",Author,0.0,0.00173913043478,0.00133739273647 332 | 330,"Kramers, J. D.",Author,0.0,0.00173913043478,0.00133739273647 333 | 331,"Morrison, R.",Author,0.0,0.00173913043478,0.000848161729492 334 | 332,"McCammon, D.",Author,0.0,0.00173913043478,0.000848161729492 335 | 333,"Wiringa, R. B.",Author,0.0,0.00173913043478,0.00120448661159 336 | 334,"Stoks, V. G. J.",Author,0.0,0.00173913043478,0.00120448661159 337 | 335,"Schiavilla, R.",Author,0.0,0.00173913043478,0.00120448661159 338 | 336,"Duquennoy, A.",Author,0.0,0.00173913043478,0.000955641101175 339 | 337,"Mayor, M.",Author,0.0,0.00173913043478,0.000955641101175 340 | 338,"van Wees, B. J.",Author,0.0,0.00173913043478,0.000964005058639 341 | 339,"van Houten, H.",Author,0.0,0.00173913043478,0.000964005058639 342 | 340,"Beenakker, C. W. J.",Author,0.0,0.00173913043478,0.000964005058639 343 | 341,"Williamson, J. G.",Author,0.0,0.00173913043478,0.000964005058639 344 | 342,"Kouwenhoven, L. P.",Author,0.0,0.00173913043478,0.000964005058639 345 | 343,"van der Marel, D.",Author,0.0,0.00173913043478,0.000964005058639 346 | 344,"Foxon, C. T.",Author,0.0,0.00173913043478,0.000964005058639 347 | 345,"Sanders, D. B.",Author,0.00383537999673,0.00347826086957,0.00143237241717 348 | 346,"Mirabel, I. F.",Author,0.0,0.00173913043478,0.000844627354347 349 | 347,"Mayor, Michel",Author,0.0,0.00173913043478,0.000912308285833 350 | 348,"Queloz, Didier",Author,0.0,0.00173913043478,0.000912308285833 351 | 349,"Witten, Edward",Author,0.0,0.00173913043478,0.000908910155458 352 | 350,"Wu, F. Y.",Author,0.0,0.00173913043478,0.000942684553686 353 | 351,"Perryman, M. A. C.",Author,0.0,0.00173913043478,0.000955641101175 354 | 352,"Lindegren, L.",Author,0.0,0.00173913043478,0.000955641101175 355 | 353,"Kovalevsky, J.",Author,0.0,0.00173913043478,0.000955641101175 356 | 354,"Hoeg, E.",Author,0.0,0.00173913043478,0.000955641101175 357 | 355,"Bastian, U.",Author,0.0,0.00173913043478,0.000955641101175 358 | 356,"Bernacca, P. L.",Author,0.0,0.00173913043478,0.000955641101175 359 | 357,"Crézé, M.",Author,0.0,0.00173913043478,0.000955641101175 360 | 358,"Donati, F.",Author,0.0,0.00173913043478,0.000955641101175 361 | 359,"Grenon, M.",Author,0.0,0.00173913043478,0.000955641101175 362 | 360,"Grewing, M.",Author,0.0,0.00173913043478,0.000955641101175 363 | 361,"van Leeuwen, F.",Author,0.0,0.00173913043478,0.000955641101175 364 | 362,"van der Marel, H.",Author,0.0,0.00173913043478,0.000955641101175 365 | 363,"Mignard, F.",Author,0.0,0.00173913043478,0.000955641101175 366 | 364,"Murray, C. A.",Author,0.0,0.00173913043478,0.000955641101175 367 | 365,"Le Poole, R. S.",Author,0.0,0.00173913043478,0.000955641101175 368 | 366,"Schrijver, H.",Author,0.0,0.00173913043478,0.000955641101175 369 | 367,"Turon, C.",Author,0.0,0.00173913043478,0.000955641101175 370 | 368,"Arenou, F.",Author,0.0,0.00173913043478,0.000955641101175 371 | 369,"Froeschlé, M.",Author,0.0,0.00173913043478,0.000955641101175 372 | 370,"Petersen, C. S.",Author,0.0,0.00173913043478,0.000955641101175 373 | 371,"Moore, Ben",Author,0.0,0.00173913043478,0.000848161729492 374 | 372,"Ghigna, Sebastiano",Author,0.0,0.00173913043478,0.000848161729492 375 | 373,"Governato, Fabio",Author,0.0,0.00173913043478,0.000848161729492 376 | 374,"Lake, George",Author,0.0,0.00173913043478,0.000848161729492 377 | 375,"Quinn, Thomas",Author,0.0,0.00173913043478,0.000848161729492 378 | 376,"Stadel, Joachim",Author,0.0,0.00173913043478,0.000848161729492 379 | 377,"Tozzi, Paolo",Author,0.0,0.00173913043478,0.000848161729492 380 | 378,"Bouchaud, Jean-Philippe",Author,0.0,0.00173913043478,0.000965469647454 381 | 379,"Mlawer, Eli J.",Author,0.0,0.00173913043478,0.00109816171168 382 | 380,"Taubman, Steven J.",Author,0.0,0.00173913043478,0.00109816171168 383 | 381,"Brown, Patrick D.",Author,0.0,0.00173913043478,0.00109816171168 384 | 382,"Iacono, Michael J.",Author,0.0,0.00173913043478,0.00109816171168 385 | 383,"Clough, Shepard A.",Author,0.0,0.00173913043478,0.00109816171168 386 | 384,"Freeman, K. C.",Author,0.0,0.00173913043478,0.000848161729492 387 | 385,"Wallace, P. R.",Author,0.0,0.00173913043478,0.00102727844508 388 | 386,"Mihalas, Dimitri",Author,0.0,0.00173913043478,0.000915640751368 389 | 387,"Lodders, Katharina",Author,0.0,0.00173913043478,0.000848161729492 390 | 388,"Burbidge, E. Margaret",Author,0.0,0.00173913043478,0.000942684553686 391 | 389,"Burbidge, G. R.",Author,0.0,0.00173913043478,0.000942684553686 392 | 390,"Fowler, William A.",Author,0.0,0.00173913043478,0.000942684553686 393 | 391,"Hoyle, F.",Author,0.0,0.00173913043478,0.000942684553686 394 | 392,"Blöchl, Peter E.",Author,0.0,0.00173913043478,0.00102727844508 395 | 393,"Jepsen, O.",Author,0.0,0.00173913043478,0.00102727844508 396 | 394,"Andersen, O. K.",Author,0.0,0.00173913043478,0.00102727844508 397 | 395,"King, Ivan R.",Author,0.0,0.00173913043478,0.000838241119484 398 | 396,"Gotze, W.",Author,0.0,0.00173913043478,0.00133739273647 399 | 397,"Sjogren, L.",Author,0.0,0.00173913043478,0.00133739273647 400 | 398,"Bruzual A., Gustavo",Author,0.0,0.00173913043478,0.000848161729492 401 | 399,"Charlot, Stephane",Author,0.0,0.00173913043478,0.000848161729492 402 | 400,"Goldreich, Peter",Author,0.0,0.00173913043478,0.000848161729492 403 | 401,"Julian, William H.",Author,0.0,0.00173913043478,0.000848161729492 404 | 402,"Werner, M. W.",Author,0.0,0.00173913043478,0.000928786654459 405 | 403,"Roellig, T. L.",Author,0.0,0.00173913043478,0.000928786654459 406 | 404,"Low, F. J.",Author,0.0,0.00173913043478,0.000928786654459 407 | 405,"Rieke, M.",Author,0.0,0.00173913043478,0.000928786654459 408 | 406,"Hoffmann, W. F.",Author,0.0,0.00173913043478,0.000928786654459 409 | 407,"Young, E.",Author,0.0,0.00173913043478,0.000928786654459 410 | 408,"Houck, J. R.",Author,0.0,0.00173913043478,0.000928786654459 411 | 409,"Brandl, B.",Author,0.0,0.00173913043478,0.000928786654459 412 | 410,"Fazio, G. G.",Author,0.0,0.00173913043478,0.000928786654459 413 | 411,"Hora, J. L.",Author,0.0,0.00173913043478,0.000928786654459 414 | 412,"Gehrz, R. D.",Author,0.0,0.00173913043478,0.000928786654459 415 | 413,"Helou, G.",Author,0.0,0.00173913043478,0.000928786654459 416 | 414,"Soifer, B. T.",Author,0.0307200663376,0.00347826086957,0.00151653171728 417 | 415,"Stauffer, J.",Author,0.0,0.00173913043478,0.000928786654459 418 | 416,"Keene, J.",Author,0.0,0.00173913043478,0.000928786654459 419 | 417,"Eisenhardt, P.",Author,0.0,0.00173913043478,0.000928786654459 420 | 418,"Gallagher, D.",Author,0.0,0.00173913043478,0.000928786654459 421 | 419,"Gautier, T. N.",Author,0.0,0.00173913043478,0.000928786654459 422 | 420,"Irace, W.",Author,0.0,0.00173913043478,0.000928786654459 423 | 421,"Lawrence, C. R.",Author,0.0,0.00173913043478,0.000928786654459 424 | 422,"Simmons, L.",Author,0.0,0.00173913043478,0.000928786654459 425 | 423,"Van Cleve, J. E.",Author,0.0,0.00173913043478,0.000928786654459 426 | 424,"Jura, M.",Author,0.0,0.00173913043478,0.000928786654459 427 | 425,"Cruikshank, D. P.",Author,0.0,0.00173913043478,0.000928786654459 428 | 426,"Iglesias, Carlos A.",Author,0.0,0.00173913043478,0.000848161729492 429 | 427,"Rogers, Forrest J.",Author,0.0,0.00173913043478,0.000848161729492 430 | 428,"Wilson, Robert E.",Author,0.0,0.00173913043478,0.000848161729492 431 | 429,"Devinney, Edward J.",Author,0.0,0.00173913043478,0.000848161729492 432 | 430,"Lewenstein, M.",Author,0.0,0.00173913043478,0.00109816171168 433 | 431,"Balcou, Ph.",Author,0.0,0.00173913043478,0.00109816171168 434 | 432,"Ivanov, M. Yu.",Author,0.0,0.00173913043478,0.00109816171168 435 | 433,"L'huillier, Anne",Author,0.0,0.00173913043478,0.00109816171168 436 | 434,"Corkum, P. B.",Author,0.0,0.00173913043478,0.00109816171168 437 | 435,"Vernazza, J. E.",Author,0.0,0.00173913043478,0.000928786654459 438 | 436,"Avrett, E. H.",Author,0.0,0.00173913043478,0.000928786654459 439 | 437,"Leoser, R.",Author,0.0,0.00173913043478,0.000928786654459 440 | 438,"Girardi, L.",Author,0.0,0.00173913043478,0.00101841803676 441 | 439,"Bressan, A.",Author,0.0,0.00173913043478,0.00101841803676 442 | 440,"Bertelli, G.",Author,0.0,0.00173913043478,0.00101841803676 443 | 441,"Chiosi, C.",Author,0.0,0.00173913043478,0.00101841803676 444 | 442,"Mateo, Mario L.",Author,0.0,0.00173913043478,0.000844627354347 445 | 443,"Tremonti, Christy A.",Author,0.0,0.00173913043478,0.000848161729492 446 | 444,"Kauffmann, Guinevere",Author,0.00243523385967,0.00347826086957,0.001426141608 447 | 445,"Brinchmann, Jarle",Author,0.00243523385967,0.00347826086957,0.001426141608 448 | 446,"Charlot, Stéphane",Author,0.00243523385967,0.00347826086957,0.001426141608 449 | 447,"Seibert, Mark",Author,0.0,0.00173913043478,0.000848161729492 450 | 448,"Peng, Eric W.",Author,0.0,0.00173913043478,0.000848161729492 451 | 449,"Uomoto, Alan",Author,0.0,0.00173913043478,0.000848161729492 452 | 450,"Fukugita, Masataka",Author,0.00243523385967,0.00347826086957,0.001426141608 453 | 451,"Brinkmann, Jon",Author,0.00243523385967,0.00347826086957,0.001426141608 454 | 452,"Zurek, Wojciech Hubert",Author,0.0,0.00173913043478,0.000942684553686 455 | 453,"Madau, Piero",Author,0.0,0.00173913043478,0.000838396545174 456 | 454,"Dickinson, Mark E.",Author,0.0,0.00173913043478,0.000838396545174 457 | 455,"Fruchter, Andrew",Author,0.0,0.00173913043478,0.000838396545174 458 | 456,"Eggen, O. J.",Author,0.0,0.00173913043478,0.000848161729492 459 | 457,"Lynden-Bell, D.",Author,0.00243523385967,0.00347826086957,0.001426141608 460 | 458,"Sandage, A. R.",Author,0.0,0.00173913043478,0.000848161729492 461 | 459,"Parker, E. N.",Author,0.00681911110149,0.00347826086957,0.00150338581419 462 | 460,"Hernquist, Lars",Author,0.0,0.00173913043478,0.000848161729492 463 | 461,"Gibbons, G. W.",Author,0.0,0.00173913043478,0.000908910155458 464 | 462,"Ferland, G. J.",Author,0.0,0.00173913043478,0.00095955402315 465 | 463,"Korista, K. T.",Author,0.0,0.00173913043478,0.00095955402315 466 | 464,"Verner, D. A.",Author,0.0,0.00173913043478,0.00095955402315 467 | 465,"Ferguson, J. W.",Author,0.0,0.00173913043478,0.00095955402315 468 | 466,"Kingdon, J. B.",Author,0.0,0.00173913043478,0.00095955402315 469 | 467,"Verner, E. M.",Author,0.0,0.00173913043478,0.00095955402315 470 | 468,"Bessell, M. S.",Author,0.0,0.00173913043478,0.00095955402315 471 | 469,"Brett, J. M.",Author,0.0,0.00173913043478,0.00095955402315 472 | 470,"Elias, J. H.",Author,0.0,0.00173913043478,0.000848161729492 473 | 471,"Madore, B. F.",Author,0.0,0.00173913043478,0.000848161729492 474 | 472,"Matthews, K.",Author,0.0,0.00173913043478,0.000848161729492 475 | 473,"Neugebauer, G.",Author,0.0,0.00173913043478,0.000848161729492 476 | 474,"Scoville, N. Z.",Author,0.0,0.00173913043478,0.000848161729492 477 | 475,"Sheth, Ravi K.",Author,0.0,0.00173913043478,0.000838396545174 478 | 476,"Tormen, Giuseppe",Author,0.0,0.00173913043478,0.000838396545174 479 | 477,"Shifman, M. A.",Author,0.0,0.00173913043478,0.00109816171168 480 | 478,"Vainshtein, A. I.",Author,0.0,0.00173913043478,0.00109816171168 481 | 479,"Zakharov, V. I.",Author,0.0,0.00173913043478,0.00109816171168 482 | 480,"Edvardsson, B.",Author,0.0,0.00173913043478,0.000955641101175 483 | 481,"Andersen, J.",Author,0.0,0.00173913043478,0.000955641101175 484 | 482,"Gustafsson, B.",Author,0.0,0.00173913043478,0.000955641101175 485 | 483,"Lambert, D. L.",Author,0.0,0.00173913043478,0.000955641101175 486 | 484,"Nissen, P. E.",Author,0.0,0.00173913043478,0.000955641101175 487 | 485,"Tomkin, J.",Author,0.0,0.00173913043478,0.000955641101175 488 | 486,"Scherrer, P. H.",Author,0.0,0.00173913043478,0.00100001565023 489 | 487,"Bogart, R. S.",Author,0.0,0.00173913043478,0.00100001565023 490 | 488,"Bush, R. I.",Author,0.0,0.00173913043478,0.00100001565023 491 | 489,"Hoeksema, J. T.",Author,0.0,0.00173913043478,0.00100001565023 492 | 490,"Kosovichev, A. G.",Author,0.0,0.00173913043478,0.00100001565023 493 | 491,"Schou, J.",Author,0.0,0.00173913043478,0.00100001565023 494 | 492,"Rosenberg, W.",Author,0.0,0.00173913043478,0.00100001565023 495 | 493,"Springer, L.",Author,0.0,0.00173913043478,0.00100001565023 496 | 494,"Tarbell, T. D.",Author,0.0,0.00173913043478,0.00100001565023 497 | 495,"Title, A.",Author,0.0,0.00173913043478,0.00100001565023 498 | 496,"Wolfson, C. J.",Author,0.0,0.00173913043478,0.00100001565023 499 | 497,"Zayer, I.",Author,0.0,0.00173913043478,0.00100001565023 500 | 498,MDI Engineering Team,Author,0.0,0.00173913043478,0.00100001565023 501 | 499,"Tully, R. B.",Author,0.0,0.00173913043478,0.000955641101175 502 | 500,"Fisher, J. R.",Author,0.0,0.00173913043478,0.000955641101175 503 | 501,"Pilbratt, G. L.",Author,0.0,0.00173913043478,0.000955641101175 504 | 502,"Riedinger, J. R.",Author,0.0,0.00173913043478,0.000955641101175 505 | 503,"Passvogel, T.",Author,0.0,0.00173913043478,0.000955641101175 506 | 504,"Crone, G.",Author,0.0,0.00173913043478,0.000955641101175 507 | 505,"Doyle, D.",Author,0.0,0.00173913043478,0.000955641101175 508 | 506,"Gageur, U.",Author,0.0,0.00173913043478,0.000955641101175 509 | 507,"Heras, A. M.",Author,0.0,0.00173913043478,0.000955641101175 510 | 508,"Jewell, C.",Author,0.0,0.00173913043478,0.000955641101175 511 | 509,"Metcalfe, L.",Author,0.0,0.00173913043478,0.000955641101175 512 | 510,"Ott, S.",Author,0.0,0.00173913043478,0.000955641101175 513 | 511,"Schmidt, M.",Author,0.0,0.00173913043478,0.000955641101175 514 | 512,"Klypin, Anatoly",Author,0.0,0.00173913043478,0.000848161729492 515 | 513,"Kravtsov, Andrey V.",Author,0.0,0.00173913043478,0.000848161729492 516 | 514,"Valenzuela, Octavio",Author,0.0,0.00173913043478,0.000848161729492 517 | 515,"Prada, Francisco",Author,0.0,0.00173913043478,0.000848161729492 518 | 516,"Seiberg, N.",Author,0.0,0.00173913043478,0.00109816171168 519 | 517,"Witten, E.",Author,0.0,0.00173913043478,0.00109816171168 520 | 518,"Wolf, Alan",Author,0.0,0.00173913043478,0.00113803354915 521 | 519,"Swift, Jack B.",Author,0.0,0.00173913043478,0.00113803354915 522 | 520,"Swinney, Harry L.",Author,0.0,0.00173913043478,0.00113803354915 523 | 521,"Vastano, John A.",Author,0.0,0.00173913043478,0.00113803354915 524 | 522,"Miller, Glenn E.",Author,0.0,0.00173913043478,0.000928786654459 525 | 523,"Scalo, John M.",Author,0.0,0.00173913043478,0.000928786654459 526 | 524,"Tremonti, Christy",Author,0.0,0.00173913043478,0.000838396545174 527 | 525,"Ridgway, Susan E.",Author,0.0,0.00173913043478,0.000838396545174 528 | 526,"Hall, Patrick B.",Author,0.0,0.00173913043478,0.000838396545174 529 | 527,"Ivezić, Željko",Author,0.0,0.00173913043478,0.000838396545174 530 | 528,"Richards, Gordon T.",Author,0.0,0.00173913043478,0.000838396545174 531 | 529,"Schneider, Donald P.",Author,0.0,0.00173913043478,0.000838396545174 532 | 530,"Abell, George O.",Author,0.0,0.00173913043478,0.000928786654459 533 | 531,"Larson, R. B.",Author,0.0,0.00173913043478,0.000838396545174 534 | 532,"Pringle, J. E.",Author,0.0036703975868,0.00347826086957,0.00142260723285 535 | 533,"Emery, V. J.",Author,0.0,0.00173913043478,0.000964005058639 536 | 534,"Scalo, J. M.",Author,0.0,0.00173913043478,0.00173611111111 537 | 535,"Bardeen, James M.",Author,0.0,0.00173913043478,0.000908910155458 538 | 536,"Steinhardt, Paul J.",Author,0.0,0.00173913043478,0.000908910155458 539 | 537,"Turner, Michael S.",Author,0.0,0.00173913043478,0.000908910155458 540 | 538,Physical Review B,Journal,0.000218148765339,0.015652173913,0.00811560510537 541 | 539,The Astrophysical Journal,Journal,0.271624680866,0.24347826087,0.0967450052887 542 | 540,The Astronomical Journal,Journal,0.0641455190772,0.0869565217391,0.0339683269908 543 | 541,The Astrophysical Journal Supplement Series,Journal,0.121021555176,0.121739130435,0.0550097284849 544 | 542,Astronomy and Astrophysics,Journal,0.00655052264808,0.0817391304348,0.0384182015781 545 | 543,Nature Materials,Journal,1.21193758521e-05,0.00347826086957,0.00225389863548 546 | 544,Geochimica et Cosmochimica Acta,Journal,0.00200575670353,0.00347826086957,0.00183459941081 547 | 545,Communications in Mathematical Physics,Journal,0.0,0.00173913043478,0.00101223063134 548 | 546,Reviews of Modern Physics,Journal,0.0159793970611,0.0765217391304,0.0352961570149 549 | 547,Astronomy and Astrophysics Supplement Series,Journal,0.000272685956673,0.0173913043478,0.00891304185466 550 | 548,Monthly Notices of the Royal Astronomical Society,Journal,0.058326634284,0.0713043478261,0.0278614123073 551 | 549,,Journal,0.0139275193994,0.015652173913,0.00693383730079 552 | 550,Physics Letters B,Journal,9.08953188911e-06,0.00521739130435,0.00222129724298 553 | 551,Publications of the Astronomical Society of the Pacific,Journal,0.0275291622481,0.0260869565217,0.0123310723352 554 | 552,Nature,Journal,0.0410309142609,0.0417391304348,0.0183959802328 555 | 553,Annual Review of Astronomy and Astrophysics,Journal,0.0302140419575,0.0347826086957,0.0137376136727 556 | 554,Physica B Condensed Matter,Journal,0.0,0.00173913043478,0.00173611111111 557 | 555,Physical Review Letters,Journal,0.00664141796697,0.0295652173913,0.0140636562502 558 | 556,Physical Review D,Journal,0.000518103317679,0.0208695652174,0.00915173620256 559 | 557,Physical Review,Journal,0.000218148765339,0.015652173913,0.00811560510537 560 | 558,Astronomical Data Analysis Software and Systems V,Journal,0.0,0.00173913043478,0.00173611111111 561 | 559,Chinese Physics C,Journal,1.21193758521e-05,0.00347826086957,0.00225389863548 562 | 560,International Journal of Modern Physics D,Journal,1.81790637782e-05,0.00521739130435,0.00333098460968 563 | 561,Physics Reports,Journal,0.00627177700348,0.0278260869565,0.0132639660166 564 | 562,Journal of Computational Physics,Journal,0.0,0.00173913043478,0.00173611111111 565 | 563,ESA Special Publication,Journal,0.0,0.00173913043478,0.00173611111111 566 | 564,Annals of Physics,Journal,0.0,0.00173913043478,0.00173611111111 567 | 565,Science,Journal,0.0,0.00173913043478,0.00121832358674 568 | 566,Earth and Planetary Science Letters,Journal,6.05968792607e-06,0.00347826086957,0.00253354786039 569 | 567,Physical Review C,Journal,1.81790637782e-05,0.00521739130435,0.00333098460968 570 | 568,Journal of Geophysical Research,Journal,6.05968792607e-05,0.00869565217391,0.00492585810824 571 | 569,European Physical Journal C,Journal,0.0,0.00173913043478,0.00121832358674 572 | 570,Reports on Progress in Physics,Journal,6.05968792607e-06,0.00347826086957,0.00253354786039 573 | 571,Physical Review A,Journal,6.05968792607e-05,0.00869565217391,0.00492585810824 574 | 572,Nuclear Physics B,Journal,6.05968792607e-05,0.00869565217391,0.00492585810824 575 | 573,Solar Physics,Journal,0.000472655658234,0.0226086956522,0.0113053521025 576 | 574,Physica D Nonlinear Phenomena,Journal,3.63581275564e-05,0.00695652173913,0.00412842135896 577 | 575,Fundamentals of Cosmic Physics,Journal,0.0,0.00173913043478,0.00173611111111 578 | -------------------------------------------------------------------------------- /IPySig_demo_data/stars_test_graph.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 65, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import pandas as pd \n", 12 | "import networkx as nx \n", 13 | "import pickle" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 14, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "df_n = pd.read_csv('stars_main_nodes.csv')\n", 23 | "df_e = pd.read_csv('stars_main_edges.csv')" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 30, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "# making a simple test graph from stars node/edge csv " 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 67, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "G = nx.from_pandas_dataframe(df_e, 'source', 'target')" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 68, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "node_attr_list = df_n.to_dict('records')" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 69, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "n_id = 0\n", 60 | "for i in G.nodes_iter(data=True):\n", 61 | " i[1]['label'] = node_attr_list[n_id]['label']\n", 62 | " i[1]['node_type'] = node_attr_list[n_id]['node_type']\n", 63 | " n_id += 1" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 70, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "pickle.dump(G,open('stars_test_graph.pkl','wb'))" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 74, 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "data": { 82 | "text/plain": [ 83 | "['Author', 'Journal']" 84 | ] 85 | }, 86 | "execution_count": 74, 87 | "metadata": {}, 88 | "output_type": "execute_result" 89 | } 90 | ], 91 | "source": [ 92 | "df_n['node_type'].unique().tolist()" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 82, 98 | "metadata": {}, 99 | "outputs": [ 100 | { 101 | "name": "stdout", 102 | "output_type": "stream", 103 | "text": [ 104 | "#2B1FE5\n" 105 | ] 106 | } 107 | ], 108 | "source": [ 109 | "import random\n", 110 | "r = lambda: random.randint(0,255)\n", 111 | "print('#%02X%02X%02X' % (r(),r(),r()))" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 85, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "gg = nx.Graph()\n", 121 | "\n", 122 | "gg.add_edge(0,1)\n", 123 | "gg.add_edge(1,2)\n", 124 | "gg.add_edge(1,3)\n", 125 | "gg.add_node(0,{'node_type':'A', 'label':'node0'})\n", 126 | "gg.add_node(1,{'node_type':'A', 'label':'node1'})\n", 127 | "gg.add_node(2,{'node_type': 'B', 'label':'node2'})\n", 128 | "gg.add_node(2,{'node_type': 'B', 'label':'node2'})" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 87, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "data": { 138 | "text/plain": [ 139 | "[(0, {'node_type': 'A'}), (1, {'node_type': 'B'}), (2, {'node_type': 'C'})]" 140 | ] 141 | }, 142 | "execution_count": 87, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "gg.nodes(data=True)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 101, 154 | "metadata": {}, 155 | "outputs": [ 156 | { 157 | "data": { 158 | "text/plain": [ 159 | "True" 160 | ] 161 | }, 162 | "execution_count": 101, 163 | "metadata": {}, 164 | "output_type": "execute_result" 165 | } 166 | ], 167 | "source": [ 168 | "df_n.iloc[:1]['node_type'][0] == df_n.iloc[:1]['node_type'][0]" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": null, 174 | "metadata": { 175 | "collapsed": true 176 | }, 177 | "outputs": [], 178 | "source": [] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "metadata": { 184 | "collapsed": true 185 | }, 186 | "outputs": [], 187 | "source": [] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": { 193 | "collapsed": true 194 | }, 195 | "outputs": [], 196 | "source": [] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "collapsed": true 203 | }, 204 | "outputs": [], 205 | "source": [] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": { 211 | "collapsed": true 212 | }, 213 | "outputs": [], 214 | "source": [] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": { 220 | "collapsed": true 221 | }, 222 | "outputs": [], 223 | "source": [] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "metadata": { 229 | "collapsed": true 230 | }, 231 | "outputs": [], 232 | "source": [] 233 | } 234 | ], 235 | "metadata": { 236 | "kernelspec": { 237 | "display_name": "Python 2", 238 | "language": "python", 239 | "name": "python2" 240 | }, 241 | "language_info": { 242 | "codemirror_mode": { 243 | "name": "ipython", 244 | "version": 2 245 | }, 246 | "file_extension": ".py", 247 | "mimetype": "text/x-python", 248 | "name": "python", 249 | "nbconvert_exporter": "python", 250 | "pygments_lexer": "ipython2", 251 | "version": "2.7.13" 252 | } 253 | }, 254 | "nbformat": 4, 255 | "nbformat_minor": 2 256 | } 257 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 John DeBlase, Daina Bouquin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IPySigma-Demo 2 | #### current version: 0.1.4 3 | 4 | This is a demo version of the IPySigma jupyter/node.js application for graph visualization. 5 | 6 | IPySigma is a lightweight python package coupled with a node-express/socket.io app. It is designed to support a seamless workflow for graph visualization in jupyter notebook by using the jupyterlab/services javscript library to leverage communication between networkx objects and sigma.js. 7 | 8 | ## Manual Install: 9 | `git clone` this repo and install both the python and node components. 10 | 11 | 12 | ### Python 13 | 14 | The prototype python package is contained in the `ipysig` folder. 15 | 16 | 1. From the root directory: Build and activate a clean python environment>=2.7.10 with requirements.txt using `virtualenv`. 17 | 18 | 2. `pip install -r requirements.txt` to get the required packages. 19 | 20 | ### Node.js 21 | 22 | The node-express application is contained in the app folder. 23 | 24 | 1. Make sure your node version is >= v6.9.4 and that both `npm` and `bower` are installed globally. 25 | 26 | 2. From the root directory: `cd ./app` 27 | 28 | 3. type `npm install` to install the node modules locally in the `app` top-level folder 29 | 30 | 4. From `app`: `cd ./browser` 31 | 32 | 5. type `bower install` to install the bower_components folder (note: these steps might change in future versions with browserify) 33 | 34 | ## Run the Demo 35 | 36 | 1. At the root directory launch a jupyter notebook server and run the notebook `ipysig_test.ipynb` 37 | 38 | 2. Follow the instructions for each cell :) 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/browser/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browser", 3 | "homepage": "https://github.com/bsnacks000/IPyGraphViz_testing", 4 | "authors": [ 5 | "js" 6 | ], 7 | "description": "client side of the IPySig prototype", 8 | "main": "main.js", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "bootstrap": "^3.3.7", 19 | "lokijs": "^1.4.3", 20 | "sigma.js": "https://github.com/jacomyal/sigma.js.git#^1.2.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/browser/client.js: -------------------------------------------------------------------------------- 1 | 2 | var browser_room = io('http://localhost:3000/browser') 3 | var main_room = io('http://localhost:3000/main') // connect a socket to the main room 4 | 5 | 6 | //TODO loki init here 7 | var idbAdapter = new LokiIndexedAdapter(); 8 | var db = new loki("test.db", { adapter: idbAdapter }); 9 | var responses = db.addCollection('responses'); 10 | 11 | //FIXME loki storage/lookup needs to change for graphs - 12 | // available graphs should be stored by title as some kind of selectable list view on the page 13 | // For now.. we just hit python every time we load 14 | 15 | 16 | 17 | 18 | $('#load-main-graph').submit(function(e){ 19 | e.preventDefault() 20 | 21 | var title = $('#graph-title').val(); 22 | console.log(title); 23 | 24 | 25 | query_loki('main_graph').then(function(db_query){ //query object from loki... if not in db emit request to kernel 26 | 27 | if (db_query.length != 0){ 28 | 29 | //FIXME pass here for now... should hit the memory cache/db first before emit 30 | 31 | } else { 32 | var g = responses.data[0].py_obj_name 33 | 34 | main_room.emit('get-graph', {'title': title,'graph_name': g }); //FIXME need to figure out how to properly query loki for py_obj_name 35 | } 36 | }); 37 | }); 38 | 39 | 40 | // this listener receives the python object reference pushes to loki, the browser instance uses this to connect to the python object 41 | //FIXME need to be able to be able to handle if user goes to localhost3000 without the pyobj reference 42 | browser_room.on('pyobj-ref-to-browser', (obj_ref)=>{ 43 | console.log(obj_ref); //TODO need to make sure this request is correct 44 | $('title').text(obj_ref.py_obj_name) 45 | push_py_obj_to_loki(obj_ref); 46 | }) 47 | 48 | 49 | main_room.on('message-reply', function(msg){ // handle the reply message 50 | // TODO: error check both msg status and that return value is the correct format 51 | // this parse and error checking should probably move to the server 52 | 53 | 54 | console.log(msg); 55 | 56 | var msg_title = msg.title; 57 | var m = msg.data.user_expressions[msg_title].data['text/plain'] 58 | graph_data = JSON.parse(m.replace(/'/g,"")); 59 | 60 | console.log(graph_data) 61 | 62 | //push the response into the database 63 | //push_to_loki(msg); 64 | 65 | // create custom html table header 66 | 67 | var graph_data_extra_filter = [ 68 | 'color', 'size', 'x', 'y', 69 | 'id', 'label', 'node_type', 70 | 'sigma_between_central', 'sigma_deg_central','sigma_pagerank' 71 | ] 72 | $('#htmlTable > thead').empty() 73 | $('#htmlTable > thead').append(make_table_header(extra_headers(graph_data, graph_data_extra_filter))) 74 | 75 | 76 | $('#graphTitle').text(msg.title) 77 | $("#graphContainer").empty() 78 | $("#controlContainer").css('visibility','visible') 79 | 80 | 81 | var s = make_graph(graph_data); 82 | s.startForceAtlas2(forceAtlasConfig); 83 | 84 | 85 | $("#pauseForceAtlas").on('click', function(event){ 86 | if (s.isForceAtlas2Running()) 87 | s.stopForceAtlas2(); 88 | else 89 | s.startForceAtlas2(); 90 | }); 91 | 92 | 93 | $('#toggleInspector').on('click', function(event){ 94 | $('#htmlTableWrapper').toggle(700) 95 | }); 96 | 97 | 98 | $("#htmlTable").on('click','.rm-btn', function(event){ 99 | $(this).closest('tr').remove(); 100 | }); 101 | 102 | // exports to local Downloads folder using the jquery plugin 103 | $("#exportButton").on('click',function(event){ 104 | $("#htmlTable").tableToCSV(); 105 | }); 106 | 107 | 108 | }) 109 | 110 | 111 | // remove a row from the table - remove header if empty 112 | 113 | 114 | // Loki helpers -- current version does not use these methods 115 | 116 | // loki helper functions that parses and pushes 117 | function push_py_obj_to_loki(py_obj){ 118 | responses.insert(py_obj); 119 | db.saveDatabase(); 120 | } 121 | 122 | 123 | function push_to_loki(msg){ 124 | responses.insert(msg.user_expressions); 125 | db.saveDatabase(); 126 | } 127 | 128 | function query_loki(res_var){ 129 | q_obj = {}; 130 | q_obj[res_var] = {'$contains' : 'data'}; // checks to see if key name is in collection 131 | return Promise.resolve(responses.find(q_obj)); 132 | } 133 | 134 | function show_loki_responses(){ 135 | return JSON.stringify(responses.data) 136 | } 137 | 138 | 139 | 140 | 141 | // Sigma make_graph -- this will all get moved to its own module... 142 | 143 | // code from the sigmajs API documentation... 144 | // adds neighbors method to sigma factory class -> populates allNeighborsIndex 145 | sigma.classes.graph.addMethod('neighbors', function(nodeId) { 146 | var k; 147 | var neighbors = {}; 148 | var index = this.allNeighborsIndex[nodeId] || {}; 149 | 150 | for (k in index) 151 | neighbors[k] = this.nodesIndex[k]; 152 | 153 | return neighbors; 154 | }); 155 | 156 | 157 | var forceAtlasConfig = { 158 | linLogMode: false, 159 | outboundAttractionDistribution: true, 160 | startingIterations: 12, // maybe figure out how to scale these for size of graph 161 | iterationsPerRender: 20, 162 | gravity:2.25 163 | } 164 | 165 | function make_graph(graph_data){ 166 | 167 | 168 | var s = new sigma({ 169 | graph: graph_data.graph, 170 | container: 'graphContainer', 171 | renderers: [{ 172 | container: document.getElementById('graphContainer'), 173 | type: 'canvas' 174 | }], 175 | settings: { 176 | drawEdges: true, 177 | drawLabels: false, 178 | doubleClickEnabled: false 179 | } 180 | }); 181 | 182 | 183 | // code from the sigmajs API documentation 184 | // binding events for neighbors - save original color 185 | // Calling refresh() on each callback alters the graph 186 | s.graph.nodes().forEach(function(n) { // archive original colors 187 | n.originalColor = n.color; 188 | }); 189 | 190 | s.graph.edges().forEach(function(e) { 191 | e.originalColor = e.color; 192 | }); 193 | 194 | s.bind('clickNode', function(e) { 195 | var nodeId = e.data.node.id; 196 | var toKeep = s.graph.neighbors(nodeId); // get Neighbor indexes on clicked nodes 197 | 198 | toKeep[nodeId] = e.data.node; 199 | 200 | s.graph.nodes().forEach(function(n) { // alter node colors for not clicked 201 | if (toKeep[n.id]) 202 | n.color = n.originalColor; 203 | else 204 | n.color = '#eee'; 205 | }); 206 | 207 | s.graph.edges().forEach(function(e) { // alter edge colors; transparent for not clicked, blue for clicked 208 | if (toKeep[e.source] && toKeep[e.target]) 209 | e.color = 'rgba(27, 115, 186, 0.90)'; 210 | else 211 | e.color = 'rgba(1, 1, 1, 0.1)'; 212 | }); 213 | 214 | s.refresh(); 215 | }); 216 | 217 | s.bind('clickStage', function(e) { // on single click away convert back 218 | s.graph.nodes().forEach(function(n) { 219 | n.color = n.originalColor; 220 | }); 221 | 222 | s.graph.edges().forEach(function(e) { 223 | e.color = e.originalColor; 224 | }); 225 | 226 | 227 | s.refresh(); 228 | }); 229 | 230 | 231 | // binding for html table 232 | s.bind('doubleClickNode', function(e){ 233 | 234 | var extra_filter_mask = [ 235 | 'originalColor', 'read_cam0:size','read_cam0:x', 236 | 'read_cam0:y','renderer1:size','renderer1:x','renderer1:y', 237 | 'size', 'x', 'y', 'color', 'id', 'label', 'node_type', 238 | 'sigma_between_central', 'sigma_deg_central', 'sigma_pagerank' 239 | ] 240 | 241 | var data = { 242 | id: "" + e.data.node.id + "", 243 | label: ""+ e.data.node.label + "", 244 | ntype: ""+e.data.node.node_type + "", 245 | betw: ""+e.data.node.sigma_between_central+ "", 246 | deg: ""+e.data.node.sigma_deg_central+ "", 247 | pager:""+ e.data.node.sigma_pagerank+ "" 248 | }; 249 | 250 | 251 | 252 | // append the node row 253 | $('#htmlTable > tbody').append(make_table_row(e, data, extra_columns(e, extra_filter_mask))); 254 | 255 | 256 | }); 257 | 258 | return s; // return the sigma instance to outer scope.. 259 | } 260 | 261 | 262 | 263 | function extra_headers(graph_data, graph_data_extra_filter){ 264 | 265 | var extras = Object.keys(graph_data.graph.nodes[0]).filter((header)=>{ 266 | return graph_data_extra_filter.indexOf(header) === -1; 267 | }).sort(); 268 | 269 | return extras; 270 | } 271 | 272 | 273 | function make_table_header(extra_headers){ 274 | 275 | 276 | var tr = " id label node_type betweeness" + 277 | "degreepagerank remove? "; 278 | 279 | 280 | for(var i=0; i < extra_headers.length; i++) 281 | tr += "" + extra_headers[i] + "" 282 | 283 | tr += '' 284 | return tr 285 | } 286 | 287 | 288 | 289 | function extra_columns(e, extra_filter_mask){ 290 | 291 | var extras = Object.keys(e.data.node).filter(function(header){ 292 | return extra_filter_mask.indexOf(header) === -1; 293 | }).sort(); 294 | 295 | return extras; 296 | } 297 | 298 | 299 | 300 | function make_table_row(e, data, extra_columns){ 301 | 302 | // helper to create a table row dynamically - takes in data object // returns a tr string 303 | var removeButton = "" ; 304 | var tr = "" + data.id + data.label + data.ntype + data.betw + data.deg + data.pager; 305 | 306 | for(var i=0; i < extra_columns.length; i++) 307 | tr += "" + e.data.node[extra_columns[i]] + "" 308 | 309 | tr += removeButton + '' 310 | return tr 311 | 312 | } -------------------------------------------------------------------------------- /app/browser/custom.css: -------------------------------------------------------------------------------- 1 | /* custom css here*/ 2 | /* Custom CSS mostly for the sigma canvas position, header fields and html table*/ 3 | 4 | .rm-btn { 5 | -webkit-border-radius: 8; 6 | -moz-border-radius: 8; 7 | border-radius: 8px; 8 | font-family: Arial; 9 | color: #ffffff; 10 | font-size: 16px; 11 | background: #a7b8c4; 12 | padding: 0px 4px 0px 3px; 13 | text-decoration: none; 14 | } 15 | 16 | #abstractWrapper { 17 | margin-right: 50%; 18 | width: 58%; 19 | display: none; 20 | } 21 | 22 | 23 | #downloadWrapper { 24 | width: 52%; 25 | margin-left: 48%; 26 | } 27 | 28 | #exportButton { 29 | width:42%; 30 | margin-left: 50%; 31 | } 32 | 33 | 34 | #graphContainer { 35 | position: absolute; 36 | width: 83%; /* need to leave some space for the remove buttons*/ 37 | height: 100%; 38 | margin: auto; 39 | } 40 | 41 | #controlContainer { 42 | visibility: hidden; 43 | margin-top: 10px; 44 | } 45 | 46 | #htmlTableWrapper { 47 | width: 52%; 48 | margin-left: 48%; 49 | margin-top: 20px; 50 | background-color: transparent; 51 | position: absolute; 52 | display: none; 53 | } 54 | 55 | #graphTitle{ 56 | font-size: 16px; 57 | color: black; 58 | 59 | } -------------------------------------------------------------------------------- /app/browser/extra/jquery.tabletoCSV.js: -------------------------------------------------------------------------------- 1 | jQuery.fn.tableToCSV = function() { 2 | 3 | var clean_text = function(text){ 4 | text = text.replace(/"/g, '""'); 5 | return '"'+text+'"'; 6 | }; 7 | 8 | $(this).each(function(){ 9 | var table = $(this); 10 | var caption = $(this).find('caption').text(); 11 | var title = []; 12 | var rows = []; 13 | 14 | $(this).find('tr').each(function(){ 15 | var data = []; 16 | $(this).find('th').each(function(){ 17 | var text = clean_text($(this).text()); 18 | title.push(text); 19 | }); 20 | $(this).find('td').each(function(){ 21 | var text = clean_text($(this).text()); 22 | data.push(text); 23 | }); 24 | data = data.join(","); 25 | rows.push(data); 26 | }); 27 | title = title.join(","); 28 | rows = rows.join("\n"); 29 | 30 | var csv = title + rows; 31 | var uri = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv); 32 | var download_link = document.createElement('a'); 33 | download_link.href = uri; 34 | var ts = new Date().getTime(); 35 | if(caption==""){ 36 | download_link.download = ts+".csv"; 37 | } else { 38 | download_link.download = caption+"-"+ts+".csv"; 39 | } 40 | document.body.appendChild(download_link); 41 | download_link.click(); 42 | document.body.removeChild(download_link); 43 | }); 44 | 45 | }; 46 | -------------------------------------------------------------------------------- /app/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IPySig Session 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

IPySigma: ProtoType

21 |
22 | 23 |
24 |
25 | 26 | 27 | 28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 |
41 | 42 | 43 |
44 |
45 | 46 |
47 |
48 |
49 | 50 |
51 |
52 | 53 | 64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/browser/sigma/sigma.layout.forceAtlas2.min.js: -------------------------------------------------------------------------------- 1 | (function(undefined){"use strict";function Supervisor(sigInst,options){var _this=this,workerFn=sigInst.getForceAtlas2Worker&&sigInst.getForceAtlas2Worker();if(options=options||{},_root.URL=_root.URL||_root.webkitURL,this.sigInst=sigInst,this.graph=this.sigInst.graph,this.ppn=10,this.ppe=3,this.config={},this.shouldUseWorker=options.worker!==!1&&webWorkers,this.workerUrl=options.workerUrl,this.started=!1,this.running=!1,this.shouldUseWorker){if(this.workerUrl)this.worker=new Worker(this.workerUrl);else{var blob=this.makeBlob(workerFn);this.worker=new Worker(URL.createObjectURL(blob))}this.worker.postMessage=this.worker.webkitPostMessage||this.worker.postMessage}else eval(workerFn);this.msgName=this.worker?"message":"newCoords",this.listener=function(t){_this.nodesByteArray=new Float32Array(t.data.nodes),_this.running&&(_this.applyLayoutChanges(),_this.sendByteArrayToWorker(),_this.sigInst.refresh())},(this.worker||document).addEventListener(this.msgName,this.listener),this.graphToByteArrays(),sigInst.bind("kill",function(){sigInst.killForceAtlas2()})}if("undefined"==typeof sigma)throw"sigma is not declared";var _root=this,webWorkers="Worker"in _root;Supervisor.prototype.makeBlob=function(t){var e;try{e=new Blob([t],{type:"application/javascript"})}catch(s){_root.BlobBuilder=_root.BlobBuilder||_root.WebKitBlobBuilder||_root.MozBlobBuilder,e=new BlobBuilder,e.append(t),e=e.getBlob()}return e},Supervisor.prototype.graphToByteArrays=function(){var t,e,s,r=this.graph.nodes(),i=this.graph.edges(),o=r.length*this.ppn,n=i.length*this.ppe,a={};for(this.nodesByteArray=new Float32Array(o),this.edgesByteArray=new Float32Array(n),t=e=0,s=r.length;t=0;t--)for(e in arguments[t])s[e]=arguments[t][e];return s}function s(t){var e;for(e in t)"hasOwnProperty"in t&&!t.hasOwnProperty(e)||delete t[e];return t}function r(t,e,s){s=s||{};a=t,h=e,u.nodesLength=a.length,u.edgesLength=h.length,i(s)}function i(t){u.settings=e(t,u.settings)}function o(){var t,e,s,r,i,o,n,g,d,l,c,f,y,w,v;for(s=0;s=0)k=a[s]=0){if(w=Math.sqrt(Math.pow(a[s]-p[e+7],2)+Math.pow(a[s+1]-p[e+8],2)),2*p[e+3]/w0?(v=l*a[s+6]*p[e+6]/w/w,a[s+2]+=c*v,a[s+3]+=f*v):w<0&&(v=-l*a[s+6]*p[e+6]/w,a[s+2]+=c*v,a[s+3]+=f*v):w>0&&(v=l*a[s+6]*p[e+6]/w/w,a[s+2]+=c*v,a[s+3]+=f*v),p[e+4]<0)break;e=p[e+4];continue}e=p[e+5]}else{if(p[e]>=0&&p[e]!==s&&(c=a[s]-a[p[e]],f=a[s+1]-a[p[e]+1],w=Math.sqrt(c*c+f*f),u.settings.adjustSizes?w>0?(v=l*a[s+6]*a[p[e]+6]/w/w,a[s+2]+=c*v,a[s+3]+=f*v):w<0&&(v=-l*a[s+6]*a[p[e]+6]/w,a[s+2]+=c*v,a[s+3]+=f*v):w>0&&(v=l*a[s+6]*a[p[e]+6]/w/w,a[s+2]+=c*v,a[s+3]+=f*v)),p[e+4]<0)break;e=p[e+4]}else for(l=u.settings.scalingRatio,r=0;r0?(v=l*a[r+6]*a[i+6]/w/w,a[r+2]+=c*v,a[r+3]+=f*v,a[i+2]+=c*v,a[i+3]+=f*v):w<0&&(v=100*l*a[r+6]*a[i+6],a[r+2]+=c*v,a[r+3]+=f*v,a[i+2]-=c*v,a[i+3]-=f*v)):(w=Math.sqrt(c*c+f*f),w>0&&(v=l*a[r+6]*a[i+6]/w/w,a[r+2]+=c*v,a[r+3]+=f*v,a[i+2]-=c*v,a[i+3]-=f*v));for(g=u.settings.gravity/u.settings.scalingRatio,l=u.settings.scalingRatio,s=0;s0&&(v=l*a[s+6]*g):w>0&&(v=l*a[s+6]*g/w),a[s+2]-=c*v,a[s+3]-=f*v;for(l=1*(u.settings.outboundAttractionDistribution?d:1),o=0;o0&&(v=-l*y*Math.log(1+w)/w/a[r+6]):w>0&&(v=-l*y*Math.log(1+w)/w):u.settings.outboundAttractionDistribution?w>0&&(v=-l*y/a[r+6]):w>0&&(v=-l*y)):(w=Math.sqrt(Math.pow(c,2)+Math.pow(f,2)),u.settings.linLogMode?u.settings.outboundAttractionDistribution?w>0&&(v=-l*y*Math.log(1+w)/w/a[r+6]):w>0&&(v=-l*y*Math.log(1+w)/w):u.settings.outboundAttractionDistribution?(w=1,v=-l*y/a[r+6]):(w=1,v=-l*y)),w>0&&(a[r+2]+=c*v,a[r+3]+=f*v,a[i+2]-=c*v,a[i+3]-=f*v);var W,L,F,_;if(u.settings.adjustSizes)for(s=0;su.maxForce&&(a[s+2]=a[s+2]*u.maxForce/W,a[s+3]=a[s+3]*u.maxForce/W),L=a[s+6]*Math.sqrt((a[s+4]-a[s+2])*(a[s+4]-a[s+2])+(a[s+5]-a[s+3])*(a[s+5]-a[s+3])),F=Math.sqrt((a[s+4]+a[s+2])*(a[s+4]+a[s+2])+(a[s+5]+a[s+3])*(a[s+5]+a[s+3]))/2,_=.1*Math.log(1+F)/(1+Math.sqrt(L)),a[s]=a[s]+a[s+2]*(_/u.settings.slowDown),a[s+1]=a[s+1]+a[s+3]*(_/u.settings.slowDown));else for(s=0;s { 20 | console.log('express listening on localhost:' + HTTP_PORT) 21 | }); 22 | 23 | app.use(express.static(__dirname + '/browser')); 24 | 25 | app.get('/', function (req, res) { 26 | res.sendFile(__dirname + '/browser/index.html'); 27 | }) 28 | 29 | //FIXME: proper error handling needed throughout the node server! 30 | 31 | let main_room = io.of('/main'); 32 | let py_room = io.of('/py'); 33 | let browser_room = io.of('/browser'); 34 | 35 | let connections = {}; 36 | //let current_browser_id = null; // temp variable that stores a 37 | let py_object_names = []; 38 | 39 | main_room.on('connection', (socket)=>{ 40 | 41 | 42 | services.Kernel.listRunning().then((kernelModels)=>{ //token passed in 43 | 44 | let options = {name:kernelModels[0].name } 45 | 46 | services.Kernel.connectTo(kernelModels[0].id,options).then((kernel)=>{ 47 | console.log('user ' + socket.id + ' connected to kernel: '+ kernelModels[0].id) 48 | 49 | // main sockets to communicate with Python IPySig here: 50 | socket.on('get-graph', (msg) => { 51 | 52 | // msg schema {'title': 'title_name', 'graph_name': graph_name } 53 | 54 | //TODO:: error check msg here 55 | 56 | let future = kernel.requestExecute({ code:'', 57 | user_expressions:{[`${msg.title}`]:`IPySig.export_graph_instance('${msg.graph_name}')`} 58 | }); 59 | 60 | future.onReply = function(reply){ 61 | 62 | main_room.to(socket.id).emit('message-reply', {'data': reply.content, 'title': msg.title}); // all replies get sent back through a single channel 63 | } 64 | }); 65 | 66 | socket.on('disconnect', ()=>{ 67 | console.log('user '+ socket.id + ' diconnected from kernel: '+ kernelModels[0].id) 68 | // can maybe emit here to fetch the browser data store and save it to disk 69 | 70 | 71 | }); 72 | 73 | }); 74 | //need to close connection to kernel via services.Kernel somehow 75 | 76 | }).catch((err)=>{ 77 | console.log(err); 78 | }); 79 | 80 | }); 81 | 82 | 83 | browser_room.on('connection', (socket)=>{ 84 | 85 | // this maintains a private namespace for for browser-server communication 86 | 87 | connections[socket.id] = py_object_names.pop() 88 | console.log('browser_connected: '+ socket.id) 89 | 90 | browser_room.to(socket.id).emit('pyobj-ref-to-browser', {'py_obj_name': connections[socket.id]}) 91 | }); 92 | 93 | 94 | 95 | py_room.on('connection', (socket)=>{ 96 | // this room connects with an IPySig object on the python kernel 97 | 98 | socket.on('py-object-name', (py_obj_name)=>{ 99 | 100 | let msg = { 'echo_name': py_obj_name ,'http_port': HTTP_PORT.toString()}; 101 | 102 | py_object_names.push(py_obj_name); 103 | // hits python client-side callback and send the HTTP_PORT number 104 | socket.emit('pyconnect_response', msg); 105 | }); 106 | 107 | }); 108 | 109 | 110 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipysig_prototype", 3 | "version": "0.0.1", 4 | "description": "A prototype app for IPySigma project that focuses on intercommunication between the notebook server and express", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "jd", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@jupyterlab/services": "^0.40.2", 13 | "express": "^4.15.2", 14 | "socket.io": "^1.7.3", 15 | "ws": "^2.2.1", 16 | "xmlhttprequest": "^1.8.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /assets/css/1-tools/_fonts.scss: -------------------------------------------------------------------------------- 1 | // https://fonts.googleapis.com/css?family=Raleway 2 | 3 | @import 'https://fonts.googleapis.com/css?family=Raleway:100,400,700'; 4 | 5 | // https://www.fontsquirrel.com/fonts/Latin-Modern-Roman 6 | 7 | @font-face { 8 | font-family: 'LMRomanSlant10'; 9 | src: url('fonts/lmromanslant10-regular-webfont.eot'); 10 | src: url('fonts/lmromanslant10-regular-webfont.eot?#iefix') format('embedded-opentype'), 11 | url('fonts/lmromanslant10-regular-webfont.woff') format('woff'), 12 | url('fonts/lmromanslant10-regular-webfont.ttf') format('truetype'), 13 | url('fonts/lmromanslant10-regular-webfont.svg#latin_modern_roman_slante10Rg') format('svg'); 14 | font-weight: normal; 15 | font-style: normal; 16 | } 17 | -------------------------------------------------------------------------------- /assets/css/1-tools/_mixins.sass: -------------------------------------------------------------------------------- 1 | @mixin tile 2 | position: relative 3 | width: 1440px 4 | max-width: 95% 5 | height: 600px 6 | margin: 30px auto 7 | background-color: $tile-bg 8 | overflow: hidden 9 | 10 | @media (max-width: 768px) 11 | margin: 15px auto 12 | 13 | @mixin wrapper 14 | position: relative 15 | display: flex 16 | width: 1180px 17 | max-width: 95% 18 | height: 100% 19 | margin: 0 auto 20 | align-items: center 21 | 22 | @mixin heading 23 | margin-top: 0 24 | margin-bottom: 30px 25 | font-size: 42px 26 | font-family: $raleway 27 | font-weight: 100 28 | 29 | @media (max-width: 640px) 30 | margin-bottom: 20px 31 | font-size: 30px 32 | -------------------------------------------------------------------------------- /assets/css/1-tools/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | html { 4 | font-family: sans-serif; 5 | -ms-text-size-adjust: 100%; 6 | -webkit-text-size-adjust: 100%; 7 | } 8 | 9 | body { 10 | margin: 0; 11 | } 12 | 13 | article, 14 | aside, 15 | details, 16 | figcaption, 17 | figure, 18 | footer, 19 | header, 20 | hgroup, 21 | main, 22 | menu, 23 | nav, 24 | section, 25 | summary { 26 | display: block; 27 | } 28 | 29 | audio, 30 | canvas, 31 | progress, 32 | video { 33 | display: inline-block; 34 | vertical-align: baseline; 35 | } 36 | 37 | audio:not([controls]) { 38 | display: none; 39 | height: 0; 40 | } 41 | 42 | [hidden], 43 | template { 44 | display: none; 45 | } 46 | 47 | a { 48 | background-color: transparent; 49 | } 50 | 51 | a:active, 52 | a:hover { 53 | outline: 0; 54 | } 55 | 56 | abbr[title] { 57 | border-bottom: 1px dotted; 58 | } 59 | 60 | b, 61 | strong { 62 | font-weight: bold; 63 | } 64 | 65 | dfn { 66 | font-style: italic; 67 | } 68 | 69 | h1 { 70 | font-size: 2em; 71 | margin: 0.67em 0; 72 | } 73 | 74 | mark { 75 | background: #ff0; 76 | color: #000; 77 | } 78 | 79 | small { 80 | font-size: 80%; 81 | } 82 | 83 | sub, 84 | sup { 85 | font-size: 75%; 86 | line-height: 0; 87 | position: relative; 88 | vertical-align: baseline; 89 | } 90 | 91 | sup { 92 | top: -0.5em; 93 | } 94 | 95 | sub { 96 | bottom: -0.25em; 97 | } 98 | 99 | img { 100 | border: 0; 101 | } 102 | 103 | svg:not(:root) { 104 | overflow: hidden; 105 | } 106 | 107 | figure { 108 | margin: 1em 40px; 109 | } 110 | 111 | hr { 112 | -moz-box-sizing: content-box; 113 | box-sizing: content-box; 114 | height: 0; 115 | } 116 | 117 | pre { 118 | overflow: auto; 119 | } 120 | 121 | code, 122 | kbd, 123 | pre, 124 | samp { 125 | font-family: monospace, monospace; 126 | font-size: 1em; 127 | } 128 | 129 | button, 130 | input, 131 | optgroup, 132 | select, 133 | textarea { 134 | color: inherit; 135 | font: inherit; 136 | margin: 0; 137 | } 138 | 139 | button { 140 | overflow: visible; 141 | } 142 | 143 | button, 144 | select { 145 | text-transform: none; 146 | } 147 | 148 | button, 149 | html input[type="button"], 150 | input[type="reset"], 151 | input[type="submit"] { 152 | -webkit-appearance: button; 153 | cursor: pointer; 154 | } 155 | 156 | button[disabled], 157 | html input[disabled] { 158 | cursor: default; 159 | } 160 | 161 | button::-moz-focus-inner, 162 | input::-moz-focus-inner { 163 | border: 0; 164 | padding: 0; 165 | } 166 | 167 | input { 168 | line-height: normal; 169 | } 170 | 171 | input[type="checkbox"], 172 | input[type="radio"] { 173 | box-sizing: border-box; 174 | padding: 0; 175 | } 176 | 177 | input[type="number"]::-webkit-inner-spin-button, 178 | input[type="number"]::-webkit-outer-spin-button { 179 | height: auto; 180 | } 181 | 182 | input[type="search"] { 183 | -webkit-appearance: textfield; 184 | -moz-box-sizing: content-box; 185 | -webkit-box-sizing: content-box; 186 | box-sizing: content-box; 187 | } 188 | 189 | input[type="search"]::-webkit-search-cancel-button, 190 | input[type="search"]::-webkit-search-decoration { 191 | -webkit-appearance: none; 192 | } 193 | 194 | fieldset { 195 | border: 1px solid #c0c0c0; 196 | margin: 0 2px; 197 | padding: 0.35em 0.625em 0.75em; 198 | } 199 | 200 | legend { 201 | border: 0; 202 | padding: 0; 203 | } 204 | 205 | textarea { 206 | overflow: auto; 207 | } 208 | 209 | optgroup { 210 | font-weight: bold; 211 | } 212 | 213 | table { 214 | border-collapse: collapse; 215 | border-spacing: 0; 216 | } 217 | 218 | td, 219 | th { 220 | padding: 0; 221 | } 222 | -------------------------------------------------------------------------------- /assets/css/1-tools/_vars.sass: -------------------------------------------------------------------------------- 1 | // Colors 2 | $white: #fff 3 | $black: #111 4 | $tile-bg: #efefef 5 | $letter-muted: #e3e3e3 6 | 7 | // Fonts 8 | $raleway: 'Raleway', Helvetica, sans-serif 9 | $roman: 'LMRomanSlant10', serif 10 | -------------------------------------------------------------------------------- /assets/css/2-basics/_body-element.sass: -------------------------------------------------------------------------------- 1 | body 2 | background-color: white 3 | font-size: 16px 4 | line-height: 1.6 5 | font-family: $roman 6 | color: $black 7 | -webkit-font-smoothing: antialiased 8 | -webkit-text-size-adjust: 100% 9 | 10 | @media (max-width: 415px) 11 | font-size: 14px 12 | -------------------------------------------------------------------------------- /assets/css/2-basics/_selection-colors.sass: -------------------------------------------------------------------------------- 1 | ::selection 2 | background: #FFF498 3 | 4 | ::-moz-selection 5 | background: #FFF498 6 | 7 | img::selection 8 | background: transparent 9 | 10 | img::-moz-selection 11 | background: transparent 12 | 13 | body 14 | -webkit-tap-highlight-color: #FFF498 15 | -------------------------------------------------------------------------------- /assets/css/2-basics/_typography.sass: -------------------------------------------------------------------------------- 1 | // Headings 2 | 3 | .gigantic, .huge, .large, .bigger, .big, 4 | h1, h2, h3, h4, h5, h6 5 | color: #222 6 | font-weight: bold 7 | 8 | .gigantic 9 | font-size: 110px 10 | line-height: 1.09 11 | letter-spacing: -2px 12 | 13 | .huge, h1 14 | font-size: 68px 15 | line-height: 1.05 16 | letter-spacing: -1px 17 | 18 | .large, h2 19 | font-size: 42px 20 | line-height: 1.14 21 | 22 | .bigger, h3 23 | font-size: 26px 24 | line-height: 1.38 25 | 26 | .big, h4 27 | font-size: 22px 28 | line-height: 1.38 29 | 30 | .small, small 31 | font-size: 10px 32 | line-height: 1.2 33 | 34 | 35 | // Basic Text Style 36 | 37 | p 38 | margin: 0 0 20px 0 39 | 40 | em 41 | font-style: italic 42 | 43 | strong 44 | font-weight: bold 45 | 46 | hr 47 | border: solid #ddd 48 | border-width: 1px 0 0 49 | clear: both 50 | margin: 10px 0 30px 51 | height: 0 52 | -------------------------------------------------------------------------------- /assets/css/3-modules/_nav.sass: -------------------------------------------------------------------------------- 1 | .nav 2 | visibility: hidden 3 | position: absolute 4 | top: 75px 5 | right: 0 6 | display: flex 7 | width: 100px 8 | flex-direction: column 9 | justify-content: center 10 | margin: 0 11 | padding: 0 12 | list-style: none 13 | background-color: $letter-muted 14 | border: 1px solid $black 15 | border: 1px solid rgba(17,17,17,.15) 16 | opacity: 0 17 | z-index: 10 18 | transition: opacity .3s ease-in-out, visibility 0s .3s 19 | 20 | &.open 21 | visibility: visible 22 | opacity: 1 23 | transition: opacity .3s ease-in-out 24 | 25 | &:before 26 | content: "" 27 | position: absolute 28 | top: -12px 29 | left: 50% 30 | transform: translateX(-50%) rotate(45deg) 31 | width: 25px 32 | height: 25px 33 | border: 1px solid $black 34 | border: 1px solid rgba(17,17,17,.15) 35 | background-color: $letter-muted 36 | 37 | li 38 | position: relative 39 | width: 100% 40 | text-align: center 41 | border-bottom: 1px solid $black 42 | border-bottom: 1px solid rgba(17,17,17,.15) 43 | background-color: $letter-muted 44 | overflow: hidden 45 | z-index: 10 46 | 47 | &:last-child 48 | border-bottom: none 49 | 50 | a 51 | display: block 52 | width: 100% 53 | height: 100% 54 | padding: 5px 0 55 | color: $black 56 | font-size: 12px 57 | font-family: $raleway 58 | text-transform: uppercase 59 | text-decoration: none 60 | transition: background-color .2s ease-in-out 61 | 62 | &:hover 63 | background-color: darken($letter-muted, 3%) 64 | -------------------------------------------------------------------------------- /assets/css/4-sections/_about.sass: -------------------------------------------------------------------------------- 1 | #about 2 | @include tile 3 | 4 | .wrapper 5 | @include wrapper 6 | 7 | .camera 8 | width: 45% 9 | 10 | img 11 | width: 85% 12 | 13 | .blurb 14 | width: 55% 15 | 16 | h2 17 | @include heading 18 | 19 | &:before 20 | content: "" 21 | position: relative 22 | bottom: 15px 23 | display: inline-block 24 | width: 70px 25 | height: 1px 26 | margin-right: 30px 27 | background-color: $black 28 | 29 | p 30 | max-width: 600px 31 | margin-bottom: 45px 32 | 33 | .social 34 | display: flex 35 | width: 180px 36 | justify-content: space-between 37 | 38 | a 39 | color: $white 40 | text-decoration: none 41 | 42 | i 43 | display: block 44 | width: 30px 45 | height: 30px 46 | font-size: 16px 47 | text-align: center 48 | line-height: 30px 49 | background-color: #909090 50 | transition: background-color .2s ease-in-out 51 | 52 | &:hover 53 | background-color: #020202 54 | 55 | @media (max-width: 1023px) 56 | .camera 57 | display: none 58 | 59 | .blurb 60 | display: flex 61 | width: 100% 62 | flex-direction: column 63 | align-items: flex-end 64 | text-align: right 65 | 66 | h2:before 67 | display: none 68 | 69 | @media (max-width: 640px) 70 | .blurb 71 | align-items: center 72 | text-align: center 73 | -------------------------------------------------------------------------------- /assets/css/4-sections/_featured.sass: -------------------------------------------------------------------------------- 1 | #featured 2 | @include tile 3 | 4 | .wrapper 5 | @include wrapper 6 | 7 | .blurb 8 | width: 45% 9 | 10 | &:after 11 | content: "" 12 | display: inline-block 13 | width: 1px 14 | height: 70px 15 | background-color: $black 16 | 17 | h2 18 | @include heading 19 | 20 | p 21 | max-width: 400px 22 | margin-bottom: 30px 23 | 24 | .featured 25 | width: 55% 26 | 27 | img 28 | width: 100% 29 | 30 | @media (max-width: 1023px) 31 | .blurb 32 | display: flex 33 | width: 100% 34 | flex-direction: column 35 | align-items: flex-end 36 | text-align: right 37 | 38 | &:after 39 | display: none 40 | 41 | p 42 | margin-bottom: 0 43 | 44 | .featured 45 | display: none 46 | 47 | @media (max-width: 640px) 48 | .blurb 49 | align-items: center 50 | text-align: center 51 | -------------------------------------------------------------------------------- /assets/css/4-sections/_full-slide.sass: -------------------------------------------------------------------------------- 1 | #full-slide 2 | @include tile 3 | 4 | .banner 5 | position: relative 6 | width: 100% 7 | height: 100% 8 | margin: 0 9 | padding: 0 10 | list-style: none 11 | 12 | li 13 | position: absolute 14 | top: 0 15 | left: 0 16 | right: 0 17 | bottom: 0 18 | background: 19 | position: center 20 | size: cover 21 | repeat: no-repeat 22 | opacity: 0 23 | transition: opacity .6s ease-in-out 24 | 25 | &.active 26 | opacity: 1 27 | transition: opacity .6s ease-in-out 28 | 29 | li:nth-child(1) 30 | background-image: url('../img/full-slide/thumb-1.jpg') 31 | 32 | li:nth-child(2) 33 | background-image: url('../img/full-slide/thumb-2.jpg') 34 | 35 | li:nth-child(3) 36 | background-image: url('../img/full-slide/thumb-3.jpg') 37 | 38 | i 39 | position: absolute 40 | top: 50% 41 | transform: translateY(-50%) scale(1) 42 | width: 60px 43 | height: 60px 44 | font-size: 28px 45 | text-align: center 46 | line-height: 60px 47 | background-color: $white 48 | cursor: pointer 49 | z-index: 10 50 | transition: all .2s ease-in-out 51 | 52 | &:hover 53 | transform: translateY(-50%) scale(1.1) 54 | 55 | .prev 56 | left: 30px 57 | 58 | .next 59 | right: 30px 60 | 61 | @media (max-width: 767px) 62 | i 63 | transform: translateY(-50%) scale(.7) 64 | 65 | &:hover 66 | transform: translateY(-50%) scale(.8) 67 | 68 | .prev 69 | left: 0 70 | 71 | .next 72 | right: 0 73 | -------------------------------------------------------------------------------- /assets/css/4-sections/_hero.sass: -------------------------------------------------------------------------------- 1 | #hero 2 | @include tile 3 | 4 | header 5 | position: absolute 6 | left: 50% 7 | transform: translateX(-50%) 8 | display: flex 9 | width: 95% 10 | height: 80px 11 | align-items: center 12 | justify-content: flex-end 13 | z-index: 10 14 | 15 | .nav-toggle 16 | position: relative 17 | display: flex 18 | width: 80px 19 | align-items: center 20 | justify-content: space-between 21 | cursor: pointer 22 | 23 | p 24 | margin-bottom: 0 25 | font-size: 14px 26 | font-family: $raleway 27 | text-transform: uppercase 28 | 29 | span, 30 | span:before, 31 | span:after 32 | content: "" 33 | position: relative 34 | display: block 35 | width: 23px 36 | height: 2px 37 | background-color: $black 38 | 39 | span:before 40 | bottom: 7px 41 | 42 | span:after 43 | top: 5px 44 | 45 | .wrapper 46 | @include wrapper 47 | 48 | .welcome 49 | width: 45% 50 | 51 | &:after 52 | content: "" 53 | display: block 54 | width: 1px 55 | height: 70px 56 | background-color: $black 57 | 58 | h1 59 | @include heading 60 | 61 | p 62 | max-width: 450px 63 | margin-bottom: 30px 64 | 65 | @media (max-width: 1180px) 66 | &:after 67 | display: none 68 | 69 | p 70 | margin-bottom: 0 71 | 72 | .photographer 73 | width: 55% 74 | align-self: flex-end 75 | line-height: 0 76 | 77 | img 78 | width: 100% 79 | opacity: .95 80 | 81 | @media (max-width: 1023px) 82 | .welcome 83 | display: flex 84 | width: 100% 85 | flex-direction: column 86 | align-items: flex-end 87 | text-align: right 88 | 89 | .photographer 90 | display: none 91 | 92 | @media (max-width: 640px) 93 | .welcome 94 | align-items: center 95 | text-align: center 96 | -------------------------------------------------------------------------------- /assets/css/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/css/fonts/icomoon.eot -------------------------------------------------------------------------------- /assets/css/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/css/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/css/fonts/icomoon.ttf -------------------------------------------------------------------------------- /assets/css/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/css/fonts/icomoon.woff -------------------------------------------------------------------------------- /assets/css/fonts/lmromanslant10-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/css/fonts/lmromanslant10-regular-webfont.eot -------------------------------------------------------------------------------- /assets/css/fonts/lmromanslant10-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/css/fonts/lmromanslant10-regular-webfont.ttf -------------------------------------------------------------------------------- /assets/css/fonts/lmromanslant10-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/css/fonts/lmromanslant10-regular-webfont.woff -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "https://fonts.googleapis.com/css?family=Raleway:100,400,700";@font-face{font-family:'LMRomanSlant10';src:url("fonts/lmromanslant10-regular-webfont.eot");src:url("fonts/lmromanslant10-regular-webfont.eot?#iefix") format("embedded-opentype"),url("fonts/lmromanslant10-regular-webfont.woff") format("woff"),url("fonts/lmromanslant10-regular-webfont.ttf") format("truetype"),url("fonts/lmromanslant10-regular-webfont.svg#latin_modern_roman_slante10Rg") format("svg");font-weight:normal;font-style:normal}/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@font-face{font-family:'icomoon';src:url("fonts/icomoon.eot?3hi6ws");src:url("fonts/icomoon.eot?3hi6ws#iefix") format("embedded-opentype"),url("fonts/icomoon.ttf?3hi6ws") format("truetype"),url("fonts/icomoon.woff?3hi6ws") format("woff"),url("fonts/icomoon.svg?3hi6ws#icomoon") format("svg");font-weight:normal;font-style:normal}[class^="icon-"],[class*=" icon-"]{font-family:'icomoon' !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-chevron-thin-left:before{content:"\e904"}.icon-chevron-thin-right:before{content:"\e905"}.icon-pinterest-p:before{content:"\e900"}.icon-twitter:before{content:"\e901"}.icon-instagram:before{content:"\e902"}.icon-facebook:before{content:"\e903"}body{background-color:#fff;font-size:16px;line-height:1.6;font-family:"LMRomanSlant10",serif;color:#111;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%}@media (max-width: 415px){body{font-size:14px}}::-moz-selection{background:#FFF498}::selection{background:#FFF498}::-moz-selection{background:#FFF498}img::-moz-selection{background:transparent}img::selection{background:transparent}img::-moz-selection{background:transparent}body{-webkit-tap-highlight-color:#FFF498}.gigantic,.huge,.large,.bigger,.big,h1,h2,h3,h4,h5,h6{color:#222;font-weight:bold}.gigantic{font-size:110px;line-height:1.09;letter-spacing:-2px}.huge,h1{font-size:68px;line-height:1.05;letter-spacing:-1px}.large,h2{font-size:42px;line-height:1.14}.bigger,h3{font-size:26px;line-height:1.38}.big,h4{font-size:22px;line-height:1.38}.small,small{font-size:10px;line-height:1.2}p{margin:0 0 20px 0}em{font-style:italic}strong{font-weight:bold}hr{border:solid #ddd;border-width:1px 0 0;clear:both;margin:10px 0 30px;height:0}svg{position:absolute;height:100%}svg path{fill:#e3e3e3}#hero svg{left:10px}#about svg,#featured svg,#three-slide svg{left:90px}#full-slide svg{left:0;opacity:.1;z-index:9}#contact svg{left:0}@media (max-width: 640px){#hero svg,#about svg,#featured svg,#three-slide svg,#full-slide svg,#contact svg{left:0}}.nav{visibility:hidden;position:absolute;top:75px;right:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;margin:0;padding:0;list-style:none;background-color:#e3e3e3;border:1px solid #111;border:1px solid rgba(17,17,17,0.15);opacity:0;z-index:10;-webkit-transition:opacity .3s ease-in-out,visibility 0s .3s;transition:opacity .3s ease-in-out,visibility 0s .3s}.nav.open{visibility:visible;opacity:1;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.nav:before{content:"";position:absolute;top:-12px;left:50%;-webkit-transform:translateX(-50%) rotate(45deg);transform:translateX(-50%) rotate(45deg);width:25px;height:25px;border:1px solid #111;border:1px solid rgba(17,17,17,0.15);background-color:#e3e3e3}.nav li{position:relative;width:100%;text-align:center;border-bottom:1px solid #111;border-bottom:1px solid rgba(17,17,17,0.15);background-color:#e3e3e3;overflow:hidden;z-index:10}.nav li:last-child{border-bottom:none}.nav li a{display:block;width:100%;height:100%;padding:5px 0;color:#111;font-size:12px;font-family:"Raleway",Helvetica,sans-serif;text-transform:uppercase;text-decoration:none;-webkit-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out}.nav li a:hover{background-color:#dbdbdb}#hero{position:relative;width:1440px;max-width:95%;height:600px;margin:30px auto;background-color:#efefef;overflow:hidden}@media (max-width: 768px){#hero{margin:15px auto}}#hero header{position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:95%;height:80px;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;z-index:10}#hero header .nav-toggle{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:80px;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;cursor:pointer}#hero header .nav-toggle p{margin-bottom:0;font-size:14px;font-family:"Raleway",Helvetica,sans-serif;text-transform:uppercase}#hero header .nav-toggle span,#hero header .nav-toggle span:before,#hero header .nav-toggle span:after{content:"";position:relative;display:block;width:23px;height:2px;background-color:#111}#hero header .nav-toggle span:before{bottom:7px}#hero header .nav-toggle span:after{top:5px}#hero .wrapper{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:1180px;max-width:95%;height:100%;margin:0 auto;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}#hero .wrapper .welcome{width:45%}#hero .wrapper .welcome:after{content:"";display:block;width:1px;height:70px;background-color:#111}#hero .wrapper .welcome h1{margin-top:0;margin-bottom:30px;font-size:42px;font-family:"Raleway",Helvetica,sans-serif;font-weight:100}@media (max-width: 640px){#hero .wrapper .welcome h1{margin-bottom:20px;font-size:30px}}#hero .wrapper .welcome p{max-width:450px;margin-bottom:30px}@media (max-width: 1180px){#hero .wrapper .welcome:after{display:none}#hero .wrapper .welcome p{margin-bottom:0}}#hero .wrapper .photographer{width:55%;-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;line-height:0}#hero .wrapper .photographer img{width:100%;opacity:.95}@media (max-width: 1023px){#hero .wrapper .welcome{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:end;-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;text-align:right}#hero .wrapper .photographer{display:none}}@media (max-width: 640px){#hero .wrapper .welcome{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;text-align:center}}#about{position:relative;width:1440px;max-width:95%;height:600px;margin:30px auto;background-color:#efefef;overflow:hidden}@media (max-width: 768px){#about{margin:15px auto}}#about .wrapper{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:1180px;max-width:95%;height:100%;margin:0 auto;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}#about .wrapper .camera{width:45%}#about .wrapper .camera img{width:85%}#about .wrapper .blurb{width:55%}#about .wrapper .blurb h2{margin-top:0;margin-bottom:30px;font-size:42px;font-family:"Raleway",Helvetica,sans-serif;font-weight:100}@media (max-width: 640px){#about .wrapper .blurb h2{margin-bottom:20px;font-size:30px}}#about .wrapper .blurb h2:before{content:"";position:relative;bottom:15px;display:inline-block;width:70px;height:1px;margin-right:30px;background-color:#111}#about .wrapper .blurb p{max-width:600px;margin-bottom:45px}#about .wrapper .blurb .social{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:180px;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}#about .wrapper .blurb .social a{color:#fff;text-decoration:none}#about .wrapper .blurb .social i{display:block;width:30px;height:30px;font-size:16px;text-align:center;line-height:30px;background-color:#909090;-webkit-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out}#about .wrapper .blurb .social i:hover{background-color:#020202}@media (max-width: 1023px){#about .wrapper .camera{display:none}#about .wrapper .blurb{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:end;-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;text-align:right}#about .wrapper .blurb h2:before{display:none}}@media (max-width: 640px){#about .wrapper .blurb{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;text-align:center}}#full-slide{position:relative;width:1440px;max-width:95%;height:600px;margin:30px auto;background-color:#efefef;overflow:hidden}@media (max-width: 768px){#full-slide{margin:15px auto}}#full-slide .banner{position:relative;width:100%;height:100%;margin:0;padding:0;list-style:none}#full-slide .banner li{position:absolute;top:0;left:0;right:0;bottom:0;background-position:center;background-size:cover;background-repeat:no-repeat;opacity:0;-webkit-transition:opacity .6s ease-in-out;transition:opacity .6s ease-in-out}#full-slide .banner li.active{opacity:1;-webkit-transition:opacity .6s ease-in-out;transition:opacity .6s ease-in-out}#full-slide .banner li:nth-child(1){background-image:url("../img/full-slide/thumb-1.jpg")}#full-slide .banner li:nth-child(2){background-image:url("../img/full-slide/thumb-2.jpg")}#full-slide .banner li:nth-child(3){background-image:url("../img/full-slide/thumb-3.jpg")}#full-slide i{position:absolute;top:50%;-webkit-transform:translateY(-50%) scale(1);transform:translateY(-50%) scale(1);width:60px;height:60px;font-size:28px;text-align:center;line-height:60px;background-color:#fff;cursor:pointer;z-index:10;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}#full-slide i:hover{-webkit-transform:translateY(-50%) scale(1.1);transform:translateY(-50%) scale(1.1)}#full-slide .prev{left:30px}#full-slide .next{right:30px}@media (max-width: 767px){#full-slide i{-webkit-transform:translateY(-50%) scale(.7);transform:translateY(-50%) scale(.7)}#full-slide i:hover{-webkit-transform:translateY(-50%) scale(.8);transform:translateY(-50%) scale(.8)}#full-slide .prev{left:0}#full-slide .next{right:0}}#featured{position:relative;width:1440px;max-width:95%;height:600px;margin:30px auto;background-color:#efefef;overflow:hidden}@media (max-width: 768px){#featured{margin:15px auto}}#featured .wrapper{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:1180px;max-width:95%;height:100%;margin:0 auto;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}#featured .wrapper .blurb{width:45%}#featured .wrapper .blurb:after{content:"";display:inline-block;width:1px;height:70px;background-color:#111}#featured .wrapper .blurb h2{margin-top:0;margin-bottom:30px;font-size:42px;font-family:"Raleway",Helvetica,sans-serif;font-weight:100}@media (max-width: 640px){#featured .wrapper .blurb h2{margin-bottom:20px;font-size:30px}}#featured .wrapper .blurb p{max-width:400px;margin-bottom:30px}#featured .wrapper .featured{width:55%}#featured .wrapper .featured img{width:100%}@media (max-width: 1023px){#featured .wrapper .blurb{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:end;-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;text-align:right}#featured .wrapper .blurb:after{display:none}#featured .wrapper .blurb p{margin-bottom:0}#featured .wrapper .featured{display:none}}@media (max-width: 640px){#featured .wrapper .blurb{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;text-align:center}}#three-slide{position:relative;width:1440px;max-width:95%;height:600px;margin:30px auto;background-color:#efefef;overflow:hidden}@media (max-width: 768px){#three-slide{margin:15px auto}}#three-slide .wrapper{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:1180px;max-width:95%;height:100%;margin:0 auto;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}#three-slide .wrapper .slider{position:relative;width:calc(100% - 60px);height:100%;margin:0 auto;padding:0;list-style:none;opacity:1;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}#three-slide .wrapper .slider.swap{opacity:0;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}#three-slide .wrapper .slider li{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:40%;opacity:0}@media (max-width: 900px){#three-slide .wrapper .slider li{width:50%}}#three-slide .wrapper .slider li img{width:100%}#three-slide .wrapper .slider .back{left:0;opacity:1}#three-slide .wrapper .slider .current{left:50%;-webkit-transform:translate(-50%, -50%) scale(1.3);transform:translate(-50%, -50%) scale(1.3);opacity:1;z-index:10}#three-slide .wrapper .slider .front{right:0;opacity:1}@media (max-width: 767px){#three-slide .wrapper .slider{width:calc(100% - 30px)}#three-slide .wrapper .slider li{width:75%}#three-slide .wrapper .slider .back,#three-slide .wrapper .slider .front{display:none}}#three-slide i{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:60px;height:60px;font-size:28px;text-align:center;line-height:60px;background-color:#fff;cursor:pointer;z-index:10;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}#three-slide i:hover{-webkit-transform:translateY(-50%) scale(1.1);transform:translateY(-50%) scale(1.1)}#three-slide .prev{left:30px}#three-slide .next{right:30px}@media (max-width: 767px){#three-slide i{-webkit-transform:translateY(-50%) scale(.7);transform:translateY(-50%) scale(.7)}#three-slide i:hover{-webkit-transform:translateY(-50%) scale(.8);transform:translateY(-50%) scale(.8)}#three-slide .prev{left:0}#three-slide .next{right:0}}#contact{position:relative;width:1440px;max-width:95%;height:600px;margin:30px auto;background-color:#efefef;overflow:hidden}@media (max-width: 768px){#contact{margin:15px auto}}#contact .wrapper{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:1180px;max-width:95%;height:100%;margin:0 auto;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}#contact .wrapper .blurb{max-width:950px;margin-top:-45px;text-align:center}#contact .wrapper .blurb h2{margin-top:0;margin-bottom:30px;font-size:42px;font-family:"Raleway",Helvetica,sans-serif;font-weight:100}@media (max-width: 640px){#contact .wrapper .blurb h2{margin-bottom:20px;font-size:30px}}#contact .wrapper .blurb p{margin-bottom:45px}#contact .wrapper form{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;max-width:600px;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;color:#111;font-family:"Raleway",Helvetica,sans-serif}#contact .wrapper form input[type="email"],#contact .wrapper form input[type="text"]{width:42%;height:40px;padding:0 15px;border:1px solid #111;border-radius:0;background:transparent}#contact .wrapper form input[type="email"]::-webkit-input-placeholder,#contact .wrapper form input[type="text"]::-webkit-input-placeholder{color:#111}#contact .wrapper form input[type="email"]::-moz-placeholder,#contact .wrapper form input[type="text"]::-moz-placeholder{color:#111}#contact .wrapper form input[type="email"]:-ms-input-placeholder,#contact .wrapper form input[type="text"]:-ms-input-placeholder{color:#111}#contact .wrapper form input[type="email"]::placeholder,#contact .wrapper form input[type="text"]::placeholder{color:#111}#contact .wrapper form input[type="email"]:focus,#contact .wrapper form input[type="text"]:focus{outline:none}#contact .wrapper form textarea{width:100%;height:90px;margin:30px 0;padding:10px 15px;border:1px solid #111;border-radius:0;background:transparent;resize:none}#contact .wrapper form textarea::-webkit-input-placeholder{color:#111}#contact .wrapper form textarea::-moz-placeholder{color:#111}#contact .wrapper form textarea:-ms-input-placeholder{color:#111}#contact .wrapper form textarea::placeholder{color:#111}#contact .wrapper form textarea:focus{outline:none}#contact .wrapper form input[type="submit"]{width:100%;height:40px;color:#fff;font-weight:700;text-transform:uppercase;border:none;border-radius:0;background-color:#111}@media (max-width: 640px){#contact .wrapper form input[type="email"],#contact .wrapper form input[type="text"]{width:100%;margin-bottom:10px}#contact .wrapper form textarea{margin:0 0 10px 0}}#contact .copyright{position:absolute;bottom:10px;left:50%;-webkit-transform:translate(-50%);transform:translate(-50%);margin-bottom:0;font-family:"Raleway",Helvetica,sans-serif;font-size:12px} 2 | -------------------------------------------------------------------------------- /assets/css/main.sass: -------------------------------------------------------------------------------- 1 | // This is the single file output by sass. It is intended to ONLY @import other files. 2 | 3 | 4 | // 1 - TOOLS 5 | @import '1-tools/fonts' 6 | @import '1-tools/normalize' 7 | @import '1-tools/icon-font' 8 | @import '1-tools/vars' 9 | @import '1-tools/mixins' 10 | 11 | 12 | // 2 - BASICS 13 | @import '2-basics/body-element' 14 | @import '2-basics/selection-colors' 15 | @import '2-basics/typography' 16 | 17 | 18 | // 3 - Modules 19 | @import '3-modules/letters' 20 | @import '3-modules/nav' 21 | 22 | 23 | // 4 - Sections 24 | @import '4-sections/hero' 25 | @import '4-sections/about' 26 | @import '4-sections/full-slide' 27 | @import '4-sections/featured' 28 | @import '4-sections/three-slide' 29 | @import '4-sections/contact' 30 | -------------------------------------------------------------------------------- /assets/img/featured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/featured.png -------------------------------------------------------------------------------- /assets/img/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/flow.png -------------------------------------------------------------------------------- /assets/img/full-slide/thumb-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/full-slide/thumb-1.jpg -------------------------------------------------------------------------------- /assets/img/full-slide/thumb-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/full-slide/thumb-2.jpg -------------------------------------------------------------------------------- /assets/img/full-slide/thumb-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/full-slide/thumb-3.jpg -------------------------------------------------------------------------------- /assets/img/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/hero.png -------------------------------------------------------------------------------- /assets/img/logos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/assets/img/logos.png -------------------------------------------------------------------------------- /assets/js/functions-min.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){function e(){$(".nav-toggle").click(function(){$(".nav").toggleClass("open")})}function s(){$('a[href^="#"]').click(function(e){var s=$($(this).attr("href"));s.length&&(e.preventDefault(),$("html, body").animate({scrollTop:s.offset().top-15},300)),$(".nav").toggleClass("open")})}function r(){$("#full-slide .prev, #full-slide .next").click(function(){var e=$(this),s=$(".banner").find(".active"),r=$(".banner").children().index(s),a=$(".banner").children().length;e.hasClass("next")?a-1>r?$(".active").removeClass("active").next().addClass("active"):$(".banner li").removeClass("active").first().addClass("active"):0===r?$(".banner li").removeClass("active").last().addClass("active"):$(".active").removeClass("active").prev().addClass("active")})}function a(){$("#three-slide .prev, #three-slide .next").click(function(){var e=$(this),s=$(".slider").find(".back"),r=$(".slider").children().index(s),a=$(".slider").find(".current"),l=$(".slider").children().index(a),n=$(".slider").find(".front"),t=$(".slider").children().index(n),d=$(".slider").children().length;$(".slider").addClass("swap"),setTimeout(function(){e.hasClass("next")?d-1>t&&d-1>l&&d-1>r?($(".back").removeClass("back").next().addClass("back"),$(".current").removeClass("current").next().addClass("current"),$(".front").removeClass("front").next().addClass("front")):t===d-1?($(".back").removeClass("back").next().addClass("back"),$(".current").removeClass("current").next().addClass("current"),$(".slider li").removeClass("front").first().addClass("front")):l===d-1?($(".back").removeClass("back").next().addClass("back"),$(".slider li").removeClass("current").first().addClass("current"),$(".front").removeClass("front").next().addClass("front")):($(".slider li").removeClass("back").first().addClass("back"),$(".current").removeClass("current").next().addClass("current"),$(".front").removeClass("front").next().addClass("front")):0!==r&&0!==l&&0!==t?($(".back").removeClass("back").prev().addClass("back"),$(".current").removeClass("current").prev().addClass("current"),$(".front").removeClass("front").prev().addClass("front")):0===r?($(".slider li").removeClass("back").last().addClass("back"),$(".current").removeClass("current").prev().addClass("current"),$(".front").removeClass("front").prev().addClass("front")):0===l?($(".back").removeClass("back").prev().addClass("back"),$(".slider li").removeClass("current").last().addClass("current"),$(".front").removeClass("front").prev().addClass("front")):($(".back").removeClass("back").prev().addClass("back"),$(".current").removeClass("current").prev().addClass("current"),$(".slider li").removeClass("front").last().addClass("front")),$(".slider").removeClass("swap")},300)})}e(),s(),r(),a()}); -------------------------------------------------------------------------------- /assets/js/functions.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | 3 | function nav(){ 4 | 5 | $('.nav-toggle').click(function(){ 6 | 7 | $('.nav').toggleClass('open'); 8 | 9 | }); 10 | 11 | } 12 | 13 | function smoothScroll(){ 14 | 15 | $('a[href^="#"]').click(function(event){ 16 | 17 | var target = $($(this).attr('href')); 18 | 19 | if (target.length){ 20 | event.preventDefault(); 21 | $('html, body').animate({ 22 | scrollTop: target.offset().top - 15 23 | }, 300); 24 | } 25 | 26 | $('.nav').toggleClass('open'); 27 | 28 | }); 29 | 30 | } 31 | 32 | function fullSlider(){ 33 | 34 | $('#full-slide .prev, #full-slide .next').click(function(){ 35 | 36 | var $this = $(this), 37 | current = $('.banner').find('.active'), 38 | position = $('.banner').children().index(current), 39 | totalPics = $('.banner').children().length; 40 | 41 | if ($this.hasClass('next')){ 42 | 43 | if (position < totalPics - 1){ 44 | $('.active').removeClass('active').next().addClass('active'); 45 | } 46 | 47 | else { 48 | $('.banner li').removeClass('active').first().addClass('active'); 49 | } 50 | 51 | } 52 | 53 | else { 54 | 55 | if (position === 0){ 56 | $('.banner li').removeClass('active').last().addClass('active'); 57 | } 58 | 59 | else { 60 | $('.active').removeClass('active').prev().addClass('active'); 61 | } 62 | 63 | } 64 | 65 | }); 66 | 67 | } 68 | 69 | function threeSlider(){ 70 | 71 | $('#three-slide .prev, #three-slide .next').click(function(){ 72 | 73 | var $this = $(this), 74 | curBack = $('.slider').find('.back'), 75 | posBack = $('.slider').children().index(curBack), 76 | curCurr = $('.slider').find('.current'), 77 | posCurr = $('.slider').children().index(curCurr), 78 | curFront = $('.slider').find('.front'), 79 | posFront = $('.slider').children().index(curFront), 80 | totalPics = $('.slider').children().length; 81 | 82 | $('.slider').addClass('swap'); 83 | 84 | setTimeout(function(){ 85 | 86 | if ($this.hasClass('next')){ 87 | 88 | if (posFront < totalPics - 1 && posCurr < totalPics - 1 && posBack < totalPics - 1){ 89 | $('.back').removeClass('back').next().addClass('back'); 90 | $('.current').removeClass('current').next().addClass('current'); 91 | $('.front').removeClass('front').next().addClass('front'); 92 | } 93 | 94 | else { 95 | 96 | if (posFront === totalPics - 1){ 97 | $('.back').removeClass('back').next().addClass('back'); 98 | $('.current').removeClass('current').next().addClass('current'); 99 | $('.slider li').removeClass('front').first().addClass('front'); 100 | } 101 | 102 | else if (posCurr === totalPics - 1){ 103 | $('.back').removeClass('back').next().addClass('back'); 104 | $('.slider li').removeClass('current').first().addClass('current'); 105 | $('.front').removeClass('front').next().addClass('front'); 106 | } 107 | 108 | else { 109 | $('.slider li').removeClass('back').first().addClass('back'); 110 | $('.current').removeClass('current').next().addClass('current'); 111 | $('.front').removeClass('front').next().addClass('front'); 112 | } 113 | 114 | } 115 | 116 | } 117 | 118 | else { 119 | 120 | if (posBack !== 0 && posCurr !== 0 && posFront !== 0){ 121 | $('.back').removeClass('back').prev().addClass('back'); 122 | $('.current').removeClass('current').prev().addClass('current'); 123 | $('.front').removeClass('front').prev().addClass('front'); 124 | } 125 | 126 | else { 127 | 128 | if (posBack === 0){ 129 | $('.slider li').removeClass('back').last().addClass('back'); 130 | $('.current').removeClass('current').prev().addClass('current'); 131 | $('.front').removeClass('front').prev().addClass('front'); 132 | } 133 | 134 | else if (posCurr === 0){ 135 | $('.back').removeClass('back').prev().addClass('back'); 136 | $('.slider li').removeClass('current').last().addClass('current'); 137 | $('.front').removeClass('front').prev().addClass('front'); 138 | } 139 | 140 | else { 141 | $('.back').removeClass('back').prev().addClass('back'); 142 | $('.current').removeClass('current').prev().addClass('current'); 143 | $('.slider li').removeClass('front').last().addClass('front'); 144 | } 145 | 146 | } 147 | 148 | } 149 | 150 | $('.slider').removeClass('swap'); 151 | 152 | }, 300); 153 | 154 | }); 155 | 156 | } 157 | 158 | nav(); 159 | 160 | smoothScroll(); 161 | 162 | fullSlider(); 163 | 164 | threeSlider(); 165 | 166 | }); 167 | -------------------------------------------------------------------------------- /images/ipysig_diagram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/images/ipysig_diagram1.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sigma-NetworkX-Jupyter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 17 | 22 |
23 |
24 |
25 |

IPySigma: a network visualization frontend for Jupyter notebooks

26 |

Create beautiful networks

27 |

An experiment from John DeBlase and Daina Bouquin

28 |
29 |
colorful networks
30 |
31 |
32 |
33 |
34 |
Jupyter SigmaJS NetworkX logos
35 |
36 |

About IPySigma

37 |

Network analytics using tools like NetworkX and Jupyter often leave programmers with difficult to examine hairballs rather than useful visualizations. Meanwhile, more flexible tools like SigmaJS have high learning curves for people new to Javascript. This extensible framework can help you create beautiful SigmaJS graphs, without ditching your Jupyter notebook.

IPySigma makes use of the following: Jupyter, Loki, SigmaJS, NetworkX.

Check out the code on GitHub

Meet John and Daina

43 |
44 |
45 |
46 |
47 | 52 |
53 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /ipysig/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.4' 2 | 3 | import logging 4 | import logging.config 5 | import sys 6 | 7 | 8 | logging.basicConfig(stream=sys.stdout, level=logging.INFO, disable_existing_loggers=False) 9 | 10 | from .core import IPySig 11 | -------------------------------------------------------------------------------- /ipysig/core.py: -------------------------------------------------------------------------------- 1 | # prototype API for IPySig networkx-sigmajs app 2 | 3 | from __future__ import print_function 4 | from __future__ import division 5 | 6 | from socketIO_client import SocketIO, BaseNamespace 7 | 8 | import os 9 | import sys 10 | import re 11 | import webbrowser as web 12 | import subprocess as sbp 13 | import time 14 | import json 15 | 16 | import networkx as nx 17 | 18 | # package imports 19 | from ipysig.sigma_addon_methods import * 20 | from ipysig.exceptions import * 21 | 22 | 23 | class Singleton(type): 24 | ''' 25 | A singleton metaclass for the IpySig controller 26 | 27 | ''' 28 | _inst = {} 29 | 30 | def __call__(cls, *args, **kwargs): 31 | 32 | if cls not in cls._inst: 33 | cls._inst[cls] = super(Singleton, cls).__call__(*args,**kwargs) 34 | 35 | return cls._inst[cls] 36 | 37 | 38 | 39 | class PyNamespace(BaseNamespace): 40 | ''' 41 | SocketIO.client namespace. callback initializes a browser 42 | ''' 43 | # callback established here 44 | def on_pyconnect_response(self, package): 45 | 46 | print('Node received: ' + package['echo_name']) # callback from 47 | web.open_new_tab('http://localhost:'+ package['http_port']) #TODO: check package for bad url 48 | time.sleep(1) # possibly not needed 49 | 50 | 51 | class IPySig(object): 52 | ''' 53 | Main graph controller object -- keeper of the express server process 54 | 55 | This is a Singleton instance. References to graphs are kept in the _store dict and called by node via a class method 56 | 57 | Example: 58 | g = nx.Graph() 59 | h = nx.Graph() 60 | 61 | ctrl = IPySig('path/to/nodeapp') # load express, init datastore and inject nx.Graph object 62 | ctrl.connect(g, 'key_name') # sets key name and graph instance in datastore, spins up the browser and passes this data to sigma app 63 | ctrl.connect(h, 'key_name') 64 | ''' 65 | 66 | __metaclass__ = Singleton # makes IPySig a singleton object 67 | 68 | _store = {} # store all instances keyed by name -- possibly used ordered dict so that node 69 | url = None # url and token of currently running notebook server 70 | token = None 71 | exp_process = None # the express process 72 | activated = False # sanity check for _sigma_api_injection call 73 | 74 | 75 | def __init__(self, node_path): 76 | # one time initialization 77 | self.node_path = node_path # TODO: error check path for validity and possibly to see if the app is listed in the proper directory 78 | 79 | if not self.__class__.activated: 80 | self._sigma_api_injector() # this injects our api into the networkx Graph class 81 | self.__class__.activated = True # Sigma networkx API is already activated - bypasses the injector 82 | 83 | if self.__class__.url is None: 84 | self._get_url_oneserver() # gets the url ## TODO Catch attribute errors if no Notebook server is running 85 | 86 | if self.__class__.exp_process is None: 87 | self.init_express() # threading and subprocess calls run_node if subprocess not running 88 | 89 | #Anything node interacts with for IPySig objects must be a class method 90 | #class methods to help interact with globals: store{}, url, and token 91 | 92 | @classmethod 93 | def export_graph_instance(cls, name): # TODO will need to check for key errors 94 | cls._store[name].sigma_make_graph() # TODO error checking 95 | export = cls._store[name].sigma_export_json() # TODO error checking 96 | 97 | return export 98 | 99 | 100 | def connect(self, graph, key_name): 101 | ''' 102 | PUBLIC - stores a graph/key_name and emits those to a running node express server 103 | :CALLS: _emit_instance_name() 104 | 105 | ''' 106 | self.__class__._store[key_name] = graph # TODO:: error check 107 | self._emit_instance_name(key_name) # send instance name and bind with browser socket id 108 | 109 | 110 | def disconnect(self, key_name): 111 | ''' 112 | PUBLIC 113 | 114 | :TODO 115 | :FIXME 116 | public method that disconnects the graph from the app 117 | -> this would require another emit using the py-room namespace method call as well as clean up code for the _store dict 118 | -> a callback could also be emitted here 119 | 120 | ''' 121 | pass 122 | 123 | def _get_url_oneserver(self): 124 | ''' 125 | gets url and token if any and sets them... run once - need to error check here 126 | current implementation hard coded to work with only one running server 127 | ''' 128 | # TODO: error checking target string 129 | out = sbp.check_output(['jupyter','notebook','list']).split('\n')[1].split(' ')[0] # pulls out single url 130 | self.__class__.url = re.match(r'http://.+/',out).group() 131 | token = re.search(r'(?<=token=).+',out) 132 | 133 | if token: 134 | self.__class__.token = token.group() 135 | 136 | def init_express(self): 137 | ''' 138 | PUBLIC 139 | calls the express app using _run_node().. not sure if this should be on a new thread.. 140 | 141 | ''' 142 | if self.__class__.exp_process is None: 143 | 144 | self.__class__.exp_process = self._run_node() # TODO:: error check 145 | print('loading express... ') 146 | time.sleep(1) 147 | print('IPySig express has started... PID: '+ str(self.__class__.exp_process.pid)) 148 | 149 | else: 150 | print('IPySIg is already running... PID: ' + str(self.__class__.exp_process)) 151 | 152 | def _emit_instance_name(self, key_name): 153 | ''' 154 | opens a socket and emits the user given instance name to express app 155 | CALLED BY: connect() 156 | 157 | ''' 158 | with SocketIO('localhost', 3000) as socket: 159 | py_namespace = socket.define(PyNamespace, '/py') 160 | py_namespace.emit('py-object-name', key_name) 161 | socket.wait(seconds=1) # blocks for one second to wait for callbacks 162 | 163 | 164 | def _run_node(self): 165 | ''' 166 | Runs the node express script with command arguments for baseUrl and token 167 | 168 | :CALLED BY: _init_express() 169 | :RETURNS: a subprocess with the node command running or None if fail 170 | 171 | ''' 172 | express_app = None 173 | 174 | node_command = ['node', self.node_path +'/index.js', '--baseUrl', self.__class__.url] 175 | if self.__class__.token: 176 | node_command.append('--token={}'.format(self.__class__.token)) # attach if running notebook has token (4.2.3+) 177 | 178 | print(' '.join(node_command)) 179 | express_app = sbp.Popen(node_command, stdout=sbp.PIPE, bufsize=1, universal_newlines=True) # TODO:: error check 180 | 181 | return express_app 182 | 183 | 184 | def kill_express_process(self): 185 | 186 | #if IPySig.exp_process is not None or IPySig.exp_process.poll() is None: 187 | 188 | if self.__class__.exp_process is not None: 189 | self.__class__.exp_process.kill() 190 | print(str(self.__class__.exp_process.pid) + ' has been killed') 191 | 192 | self.__class__.exp_process = None 193 | else: 194 | print('There is currently no IPySig.exp_process running') 195 | 196 | 197 | def _sigma_api_injector(self): 198 | ''' 199 | injects sigma API methods into the nx.Graph class 200 | CALLED BY: __init__() 201 | ''' 202 | nx.Graph.sigma_make_graph = sigma_make_graph 203 | nx.Graph.sigma_export_json = sigma_export_json 204 | 205 | nx.Graph.sigma_add_degree_centrality = sigma_add_degree_centrality 206 | nx.Graph.sigma_add_betweenness_centrality = sigma_add_betweenness_centrality 207 | nx.Graph.sigma_add_pagerank = sigma_add_pagerank 208 | 209 | nx.Graph.sigma_node_add_extra = sigma_node_add_extra 210 | nx.Graph.sigma_choose_edge_color = sigma_choose_edge_color 211 | nx.Graph.sigma_build_pandas_dfs = sigma_build_pandas_dfs 212 | nx.Graph.sigma_edge_weights = sigma_edge_weights 213 | 214 | nx.Graph.sigma_color_picker = sigma_color_picker 215 | nx.Graph.sigma_assign_node_colors = sigma_assign_node_colors 216 | 217 | nx.Graph.sigma_node_add_color_node_type = sigma_node_add_color_node_type 218 | nx.Graph.sigma_node_add_label = sigma_node_add_label 219 | 220 | -------------------------------------------------------------------------------- /ipysig/exceptions.py: -------------------------------------------------------------------------------- 1 | ''' 2 | custom exceptions for IPySigma application 3 | 4 | ''' 5 | 6 | class IPySigmaBaseException(Exception): 7 | ''' 8 | exception base class for IPySigmaPackage 9 | ''' 10 | pass 11 | 12 | 13 | class IPySigmaAddOnException(IPySigmaBaseException): 14 | ''' 15 | exception base class for sigma_add_on module 16 | ''' 17 | pass 18 | 19 | 20 | class IPySigmaCoreException(IPySigmaBaseException): 21 | ''' 22 | exception base class for SigmaCore module 23 | ''' 24 | pass 25 | 26 | 27 | class IPySigmaGraphNodeIndexError(IPySigmaAddOnException, IndexError): 28 | ''' 29 | exception thrown when graphs are empty 30 | ''' 31 | pass 32 | 33 | class IPySigmaGraphEdgeIndexError(IPySigmaAddOnException, IndexError): 34 | ''' 35 | exception thrown when graphs are empty 36 | ''' 37 | pass 38 | 39 | 40 | class IPySigmaGraphDataFrameValueError(IPySigmaAddOnException, ValueError): 41 | ''' 42 | exception thrown when build_pandas_df method returns empty dfs 43 | ''' 44 | pass 45 | 46 | class IPySigmaGraphJSONValueError(IPySigmaAddOnException, ValueError): 47 | ''' 48 | exception thrown if JSON dict is empty 49 | ''' 50 | pass 51 | 52 | 53 | class IPySigmaNodeTypeError(IPySigmaAddOnException): 54 | ''' 55 | exception thrown if 'node_type' field has not been assigned by user to node attributes 56 | ''' 57 | 58 | class IPySigmaLabelError(IPySigmaAddOnException): 59 | ''' 60 | exception thrown if 'label' field has not been assigned by the user to node attributes 61 | ''' -------------------------------------------------------------------------------- /ipysig/sigma_addon_methods.py: -------------------------------------------------------------------------------- 1 | ''' 2 | this is a module of add-on methods that get injected into the nx.Graph class within IPySig 3 | Sigma calls these on graph object instances stored in the IPySig data store 4 | 5 | ''' 6 | import pandas as pd 7 | import networkx as nx 8 | from numpy import cos, sin, pi 9 | from numpy.random import randint 10 | 11 | import json 12 | 13 | import logging 14 | logger = logging.getLogger(__name__) 15 | 16 | from exceptions import IPySigmaGraphNodeIndexError, \ 17 | IPySigmaGraphEdgeIndexError, IPySigmaGraphDataFrameValueError, \ 18 | IPySigmaGraphJSONValueError, IPySigmaNodeTypeError, IPySigmaLabelError 19 | 20 | 21 | #PUBLIC API 22 | 23 | def sigma_make_graph(self): 24 | ''' 25 | A method that serves as a rbuild script for the sigma add on methods 26 | adds centrality measures , and extra attributes that sigma needs for node and edgelists 27 | ''' 28 | try: 29 | self.sigma_add_degree_centrality() 30 | self.sigma_add_pagerank() 31 | self.sigma_add_betweenness_centrality() 32 | 33 | self.sigma_nodes, self.sigma_edges = self.sigma_build_pandas_dfs() # makes df and assigns sigma_nodes and sigma_edges instance variables 34 | 35 | 36 | if len(self.sigma_nodes.index) == 0 or len(self.sigma_nodes.columns) == 0: 37 | raise IPySigmaGraphDataFrameValueError('Sigma Node and Edge dataframes are empty') 38 | 39 | except IPySigmaGraphDataFrameValueError as err: 40 | logger.exception("Sigma Node and Edge dataframes are empty ") 41 | raise 42 | 43 | #NOTE these are handled as warnings 44 | try: 45 | self.sigma_node_add_color_node_type() 46 | 47 | except IPySigmaNodeTypeError as err: 48 | logger.warn('No node_type field detected for node attributes: using default color for all nodes') 49 | 50 | try: 51 | self.sigma_node_add_label() 52 | 53 | except IPySigmaLabelError as err: 54 | logger.warn('No label field detected for node attributes: using node id for node label') 55 | 56 | 57 | self.sigma_node_add_extra() 58 | self.sigma_edge_weights() 59 | 60 | 61 | def sigma_export_json(self): 62 | ''' 63 | sets and returns obj dictionary with node and edge list arrays(sigma compatible) 64 | assuming all goes well above, this method returns the sigma_json graph object 65 | ''' 66 | 67 | 68 | try: 69 | 70 | sigma_obj = { 'error': 'false', 'graph': {} } 71 | sigma_obj['graph']['nodes'] = self.sigma_nodes.to_dict('records') 72 | sigma_obj['graph']['edges'] = self.sigma_edges.to_dict('records') 73 | 74 | if len(sigma_obj['graph']['nodes']) == 0: 75 | raise IPySigmaGraphJSONValueError(' Sigma JSON Export Error ') 76 | 77 | 78 | except AttributeError as err: 79 | logger.warning('Sigma Node and Edges have not been set yet: make sure G.sigma_make_graph() has been called first') 80 | sigma_obj['error'] = 'true' 81 | 82 | 83 | except IPySigmaGraphJSONValueError as err: 84 | logger.warning('Sigma JSON object node and edgelists are empty') 85 | sigma_obj['error'] = 'true' 86 | 87 | 88 | return json.dumps(sigma_obj) 89 | 90 | 91 | def sigma_add_degree_centrality(self): 92 | dc = nx.degree_centrality(self) 93 | nx.set_node_attributes(self, 'sigma_deg_central',dc) 94 | 95 | 96 | def sigma_add_betweenness_centrality(self): 97 | bc = nx.betweenness_centrality(self) 98 | nx.set_node_attributes(self,'sigma_between_central',bc) 99 | 100 | 101 | def sigma_add_pagerank(self): 102 | pr = nx.pagerank(self) 103 | nx.set_node_attributes(self, 'sigma_pagerank',pr) 104 | 105 | 106 | def sigma_build_pandas_dfs(self): 107 | ''' 108 | dumps graph data into pandas dfs... returns a node and edge dataframe 109 | for nodes just need to dump attribute dictionary 110 | 111 | Throws index errors if node or edgelists are blank 112 | 113 | :returns: self.sigma_nodes, self.sigma_edges 114 | ''' 115 | 116 | try: 117 | 118 | nodelist = self.nodes(data=True) 119 | 120 | if len(nodelist) == 0: 121 | raise IPySigmaGraphNodeIndexError('nx.Graph.nodes list is empty') 122 | 123 | nodes = {} 124 | 125 | nodes['id'] = [i[0] for i in nodelist] 126 | for k in nodelist[0][1].keys(): # the api always injects at least centrality measuers 127 | nodes[k] = [i[1][k] for i in nodelist] 128 | 129 | e = self.edges(data=True) 130 | 131 | if len(e) == 0: 132 | raise IPySigmaGraphEdgeIndexError('nx.Graph.edges list is empty') 133 | 134 | edgelist = [0 for i in xrange(len(self.edges()))] # init for optimization (large edgelists) 135 | 136 | for i in range(len(edgelist)): 137 | edgelist[i] = {'id': i, 'source':e[i][0], 'target':e[i][1]} 138 | if (len(e[i][2]) != 0): # if edge weight exists 139 | edgelist[i]['weight'] = e[i][2]['weight'] 140 | 141 | 142 | except (IPySigmaGraphNodeIndexError, IPySigmaGraphEdgeIndexError) as err: 143 | logger.error("Node and Edge lists of nx.Graph object have been left blank ") 144 | raise 145 | 146 | 147 | sigma_edges = pd.DataFrame(edgelist) # these are the pandas dfs that will get exported 148 | sigma_nodes = pd.DataFrame(nodes) 149 | 150 | return sigma_nodes, sigma_edges 151 | 152 | 153 | 154 | 155 | def sigma_node_add_extra(self): 156 | 157 | # adds extra stuff to the nodes list needed for sigma 158 | node_len = len(self.sigma_nodes) 159 | 160 | # spreads nodes in a circle for forceAtlas2 161 | x = 10 * cos(2 * randint(0,node_len,node_len) * pi/node_len) 162 | y = 10 * sin(2 * randint(0,node_len,node_len) * pi/node_len) 163 | 164 | # stuff for sigma nodes 165 | self.sigma_nodes['x'] = x 166 | self.sigma_nodes['y'] = y 167 | self.sigma_nodes['size'] = 0.25 + 15 * self.sigma_nodes['sigma_deg_central'] # set to degree central 168 | 169 | 170 | 171 | def sigma_node_add_color_node_type(self): 172 | ''' 173 | adds a color field based on a node_type column: 174 | if node_type is not present a default color is chosen, node_type field is set to None and an error is raised. 175 | ''' 176 | 177 | default_node_color = '#79a55c' 178 | 179 | try: 180 | if 'node_type' not in self.sigma_nodes: 181 | raise IPySigmaNodeTypeError 182 | 183 | # clean up 184 | self.sigma_nodes['node_type'] = self.sigma_nodes['node_type'].str.replace(r"'", "") # a single quote throws off json parser 185 | self.sigma_nodes['node_type'].fillna(self.sigma_nodes['node_type'],inplace=True) 186 | 187 | colors = self.sigma_color_picker() 188 | self.sigma_nodes['color'] = self.sigma_nodes.apply(self.sigma_assign_node_colors, args=(colors,), axis=1) 189 | 190 | except IPySigmaNodeTypeError as err: 191 | self.sigma_nodes['node_type'] = 'undefined node_type' 192 | self.sigma_nodes['color'] = default_node_color 193 | raise 194 | 195 | 196 | 197 | def sigma_node_add_label(self): 198 | ''' 199 | adds a label field to sigma_nodes: 200 | if one is not present an error is thrown and caught as warning in build and the id field is used 201 | ''' 202 | try: 203 | if 'label' not in self.sigma_nodes: # assigns the label field to id if label is not provided 204 | raise IPySigmaLabelError 205 | 206 | #clean up labels 207 | self.sigma_nodes['label'] = self.sigma_nodes['label'].str.replace(r"'", "") 208 | self.sigma_nodes['label'].fillna(self.sigma_nodes['id'],inplace=True) 209 | 210 | except IPySigmaLabelError as err: 211 | self.sigma_nodes['label'] = self.sigma_nodes['id'] 212 | raise 213 | 214 | 215 | def sigma_color_picker(self): 216 | ''' 217 | helper method that picks colors based on node_type column 218 | :RETURNS: a dict of assigned node colors 219 | ''' 220 | if 'node_type' not in self.sigma_nodes: 221 | return 222 | 223 | # assign colors based for each unique value in node_type 224 | node_type_vals_list = self.sigma_nodes['node_type'].unique().tolist() 225 | node_type_color_dict = {} 226 | 227 | for node_type in node_type_vals_list: 228 | r = lambda: randint(0,255) 229 | color = '#%02X%02X%02X' % (r(),r(),r()) 230 | node_type_color_dict[node_type] = color 231 | 232 | return node_type_color_dict 233 | 234 | 235 | def sigma_assign_node_colors(self, row, color_dict): 236 | ''' 237 | callback for sigma color picker apply method 238 | ''' 239 | return color_dict[row['node_type']] 240 | 241 | 242 | 243 | def sigma_edge_weights(self): 244 | ''' 245 | if edges have weight attribute change column name to size 246 | ''' 247 | 248 | if 'weight' in self.sigma_edges.columns: 249 | self.sigma_edges['size'] = self.sigma_edges['weight'] * 2 250 | # color code edges 251 | self.sigma_edges['color'] = self.sigma_edges.apply(self.sigma_choose_edge_color, axis=1) 252 | else: 253 | self.sigma_edges['color'] = '#d3d3d3' # grey if not weighted 254 | 255 | 256 | def sigma_choose_edge_color(self,row): 257 | ''' 258 | callback for sigma_edge_weights 259 | bins the color weight and returns a darker hex value as it increases 260 | ''' 261 | 262 | if row['weight'] == 1: 263 | return 'rgba(211, 211, 211, 0.1)' 264 | if row['weight'] >= 2 and row['weight'] < 5: 265 | return 'rgba(214, 50, 50, 0.7)' 266 | if row['weight'] >= 5: 267 | return 'rgba(112, 0, 0, 0.95)' 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /ipysig/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsnacks000/IPySigma-Demo/8638682347442d08e80111e1bb68ac9babf0db99/ipysig/tests/__init__.py -------------------------------------------------------------------------------- /ipysig/tests/mocks.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A module with some mock classes that can or have been used for testing/debugging 3 | ''' 4 | import json 5 | import networkx as nx 6 | from ipysig.sigma_addon_methods import * 7 | 8 | 9 | class DummyGraph(object): 10 | ''' simulates storage of a JSON graph.. method gets called and sends to node server''' 11 | _store = {} 12 | 13 | def __init__(self): 14 | self.graph = { 15 | "graph": { 16 | "nodes": [{"id": "n0","label": "A node","x": 0,"y": 0,"size": 3},{"id": "n1","label": "Another node","x": 3,"y": 1,"size": 2},{"id": "n2","label": "And a last one","x": 1,"y": 3,"size": 1}], 17 | "edges": [{"id": "e0","source": "n0","target": "n1"},{"id": "e1","source": "n1","target": "n2"},{"id": "e2","source": "n2","target": "n0"}] 18 | } 19 | } 20 | self.name = 'dumb_graph' 21 | DummyGraph._store[self.name] = self 22 | 23 | @classmethod 24 | def get_graph(cls, name): 25 | return json.dumps(cls._store[name].graph) 26 | 27 | 28 | class InjectedGraph(object): 29 | ''' 30 | returns an injected graph for development testing if not already done 31 | 32 | ''' 33 | 34 | def __new__(cls, g=None): 35 | 36 | cls.g = g or nx.Graph() 37 | 38 | if not hasattr(cls.g,'sigma_make_graph'): 39 | 40 | nx.Graph.sigma_make_graph = sigma_make_graph 41 | nx.Graph.sigma_export_json = sigma_export_json 42 | 43 | nx.Graph.sigma_add_degree_centrality = sigma_add_degree_centrality 44 | nx.Graph.sigma_add_betweenness_centrality = sigma_add_betweenness_centrality 45 | nx.Graph.sigma_add_pagerank = sigma_add_pagerank 46 | 47 | nx.Graph.sigma_node_add_extra = sigma_node_add_extra 48 | nx.Graph.sigma_choose_edge_color = sigma_choose_edge_color 49 | nx.Graph.sigma_build_pandas_dfs = sigma_build_pandas_dfs 50 | nx.Graph.sigma_edge_weights = sigma_edge_weights 51 | 52 | return cls.g -------------------------------------------------------------------------------- /ipysig/tests/test_core.py: -------------------------------------------------------------------------------- 1 | ''' 2 | nose tests for ipysig.core.py 3 | 4 | ''' 5 | 6 | import unittest 7 | import os 8 | import networkx as nx 9 | 10 | from ..core import IPySig 11 | 12 | 13 | #TODO mock out system calls and express process for tests 14 | 15 | import unittest 16 | from mock import patch 17 | 18 | from ..core import IPySig 19 | 20 | 21 | test_path = os.path.abspath(os.path.join('..','..','app')) 22 | 23 | class TestIPySig(unittest.TestCase): 24 | 25 | 26 | @patch('ipysig.IPySig._get_url_oneserver', return_value='bypassed') 27 | @patch('ipysig.IPySig.init_express',return_value='bypassed') 28 | def test_IPySig_is_singleton(self,mock_get_url_oneserver,mock_init_express): 29 | 30 | 31 | ctrl = IPySig(test_path) 32 | ctrl2 = IPySig(test_path) 33 | 34 | self.assertTrue(ctrl is ctrl2) 35 | 36 | 37 | @patch('ipysig.IPySig._get_url_oneserver', return_value='bypassed') 38 | @patch('ipysig.IPySig.init_express',return_value='bypassed') 39 | def test_IPySig_set_to_true(self, mock_get_url_oneserver,mock_init_express): 40 | 41 | ctrl = IPySig(test_path) 42 | self.assertTrue(IPySig.activated) 43 | 44 | 45 | @patch('ipysig.IPySig._get_url_oneserver', return_value='bypassed') 46 | @patch('ipysig.IPySig.init_express',return_value='bypassed') 47 | def test_sigma_api_injector(self, mock_get_url_oneserver,mock_init_express): 48 | 49 | ctrl = IPySig(test_path) 50 | g = nx.Graph() 51 | 52 | self.assertTrue(hasattr(g, 'sigma_make_graph')) 53 | self.assertTrue(hasattr(g, 'sigma_export_json' )) 54 | self.assertTrue(hasattr(g, 'sigma_add_degree_centrality' )) 55 | self.assertTrue(hasattr(g, 'sigma_add_betweenness_centrality' )) 56 | self.assertTrue(hasattr(g, 'sigma_add_pagerank' )) 57 | self.assertTrue(hasattr(g, 'sigma_node_add_extra' )) 58 | self.assertTrue(hasattr(g, 'sigma_choose_edge_color')) 59 | self.assertTrue(hasattr(g, 'sigma_build_pandas_dfs')) 60 | self.assertTrue(hasattr(g, 'sigma_edge_weights')) 61 | -------------------------------------------------------------------------------- /ipysig/tests/test_sigma_addon_methods.py: -------------------------------------------------------------------------------- 1 | ''' 2 | nose tests for ipysig.sigma_addon_methods.py 3 | ''' 4 | 5 | import unittest 6 | import networkx as nx 7 | import pandas as pd 8 | import json 9 | 10 | from ..sigma_addon_methods import * 11 | from ..exceptions import IPySigmaGraphDataFrameValueError, \ 12 | IPySigmaGraphEdgeIndexError, IPySigmaGraphNodeIndexError, \ 13 | IPySigmaNodeTypeError, IPySigmaLabelError 14 | from .mocks import InjectedGraph 15 | 16 | import logging 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | class TestSigmaAddOns(unittest.TestCase): 21 | 22 | @classmethod 23 | def setUpClass(cls): 24 | nx.Graph.sigma_make_graph = sigma_make_graph 25 | nx.Graph.sigma_export_json = sigma_export_json 26 | 27 | nx.Graph.sigma_add_degree_centrality = sigma_add_degree_centrality 28 | nx.Graph.sigma_add_betweenness_centrality = sigma_add_betweenness_centrality 29 | nx.Graph.sigma_add_pagerank = sigma_add_pagerank 30 | 31 | nx.Graph.sigma_node_add_extra = sigma_node_add_extra 32 | nx.Graph.sigma_choose_edge_color = sigma_choose_edge_color 33 | nx.Graph.sigma_build_pandas_dfs = sigma_build_pandas_dfs 34 | nx.Graph.sigma_edge_weights = sigma_edge_weights 35 | 36 | nx.Graph.sigma_node_add_color_node_type = sigma_node_add_color_node_type 37 | nx.Graph.sigma_node_add_label = sigma_node_add_label 38 | 39 | nx.Graph.sigma_color_picker = sigma_color_picker 40 | nx.Graph.sigma_assign_node_colors = sigma_assign_node_colors 41 | 42 | def setUp(self): 43 | 44 | self.graph = nx.complete_graph(10) 45 | self.weighted_graph =nx.Graph() 46 | 47 | self.weighted_graph.add_edge('a','b',weight=0.6) 48 | self.weighted_graph.add_edge('a','c',weight=0.2) 49 | self.weighted_graph.add_edge('c','d',weight=0.1) 50 | self.weighted_graph.add_edge('c','e',weight=0.7) 51 | 52 | self.graph_attr = nx.Graph() 53 | self.graph_attr.add_edge(0,1) 54 | self.graph_attr.add_edge(1,2) 55 | self.graph_attr.add_edge(3,0) 56 | self.graph_attr.add_node(0,{'node_type':'A', 'label':'node0'}) 57 | self.graph_attr.add_node(1,{'node_type':'A', 'label':'node1'}) 58 | self.graph_attr.add_node(2,{'node_type': 'B', 'label':'node2'}) 59 | self.graph_attr.add_node(3,{'node_type': 'C', 'label':'node2'}) 60 | 61 | 62 | 63 | def tearDown(self): 64 | self.graph = None 65 | self.weighted_graph = None 66 | self.graph_attr = None 67 | 68 | def test_make_graph(self): 69 | 70 | self.graph.sigma_make_graph() 71 | self.assertTrue(hasattr(self.graph, 'sigma_edges')) 72 | self.assertTrue(hasattr(self.graph, 'sigma_nodes')) 73 | 74 | 75 | def test_degree_centrality_is_added_to_graph_obj(self): 76 | 77 | self.graph.sigma_add_degree_centrality() 78 | node_data = self.graph.nodes(data=True) 79 | 80 | for node in node_data: 81 | self.assertIn('sigma_deg_central',node[1]) 82 | 83 | 84 | def test_betweenness_centrality_is_added_to_graph_obj(self): 85 | 86 | self.graph.sigma_add_betweenness_centrality() 87 | node_data = self.graph.nodes(data=True) 88 | 89 | for node in node_data: 90 | self.assertIn('sigma_between_central',node[1]) 91 | 92 | 93 | def test_pagerank_is_added_to_graph_obj(self): 94 | 95 | self.graph.sigma_add_pagerank() 96 | node_data = self.graph.nodes(data=True) 97 | 98 | for node in node_data: 99 | self.assertIn('sigma_pagerank',node[1]) 100 | 101 | 102 | def test_sigma_build_pandas_dfs_edges_nodes_exist(self): 103 | 104 | self.graph.sigma_nodes, self.graph.sigma_edges = self.graph.sigma_build_pandas_dfs() 105 | 106 | self.assertTrue(hasattr(self.graph, 'sigma_edges')) 107 | self.assertTrue(hasattr(self.graph, 'sigma_nodes')) 108 | 109 | self.weighted_graph.sigma_nodes, self.weighted_graph.sigma_edges = self.weighted_graph.sigma_build_pandas_dfs() 110 | 111 | self.assertTrue(hasattr(self.weighted_graph, 'sigma_edges')) 112 | self.assertTrue(hasattr(self.weighted_graph, 'sigma_nodes')) 113 | 114 | 115 | def test_sigma_build_pandas_dfs_edges_nodes_not_none(self): 116 | 117 | self.graph.sigma_nodes, self.graph.sigma_edges = self.graph.sigma_build_pandas_dfs() 118 | 119 | self.assertIsNotNone(self.graph.sigma_nodes) 120 | self.assertIsNotNone(self.graph.sigma_edges) 121 | 122 | self.weighted_graph.sigma_nodes, self.weighted_graph.sigma_edges = self.weighted_graph.sigma_build_pandas_dfs() 123 | 124 | self.assertIsNotNone(self.weighted_graph.sigma_nodes) 125 | self.assertIsNotNone(self.weighted_graph.sigma_edges) 126 | 127 | 128 | def test_extras_are_added_to_graph_obj(self): 129 | 130 | self.graph.sigma_make_graph() 131 | 132 | self.assertIn('y',self.graph.sigma_nodes) 133 | self.assertIn('color',self.graph.sigma_nodes) 134 | self.assertIn('size',self.graph.sigma_nodes) 135 | self.assertIn('x', self.graph.sigma_nodes) 136 | 137 | 138 | def test_edge_weight_color_size_are_set_if_exist(self): 139 | 140 | self.weighted_graph.sigma_nodes, self.weighted_graph.sigma_edges = self.weighted_graph.sigma_build_pandas_dfs() 141 | self.weighted_graph.sigma_edge_weights() 142 | 143 | self.assertIn('size', self.weighted_graph.sigma_edges) 144 | self.assertIn('color', self.weighted_graph.sigma_edges) 145 | 146 | 147 | def test_sigma_node_df_has_id(self): 148 | 149 | self.graph.sigma_nodes, self.graph.sigma_edges = self.graph.sigma_build_pandas_dfs() 150 | self.assertIn('id',self.graph.sigma_nodes) 151 | 152 | 153 | def test_sigma_make_graph_produces_neccesary_columns(self): 154 | 155 | correct_nodes = ['id','x','y','label','node_type','color','size', 'sigma_deg_central', 'sigma_pagerank', 'sigma_between_central'] 156 | correct_edges = ['id','source', 'target', 'color'] 157 | correct_edges_weighted = ['id', 'source','target','color','weight','size'] 158 | 159 | self.graph.sigma_make_graph() 160 | self.weighted_graph.sigma_make_graph() 161 | 162 | node_col_list = self.graph.sigma_nodes.columns.tolist() 163 | edge_col_list = self.graph.sigma_edges.columns.tolist() 164 | weight_edge_col_list = self.weighted_graph.sigma_edges.columns.tolist() 165 | 166 | for n in node_col_list: 167 | self.assertIn(n, correct_nodes) 168 | 169 | for e in edge_col_list: 170 | self.assertIn(e, correct_edges) 171 | 172 | for w in weight_edge_col_list: 173 | self.assertIn(w, correct_edges_weighted) 174 | 175 | 176 | def test_sigma_node_df_contains_additional_columns_if_available(self): 177 | 178 | nx.set_node_attributes(self.graph, 'some_attr', 'xxx') 179 | 180 | self.graph.sigma_make_graph() 181 | 182 | self.assertIn('some_attr',self.graph.sigma_nodes) 183 | 184 | 185 | def test_sigma_build_pandas_dfs_raise_node_index_error_if_blank(self): 186 | 187 | blank_graph = InjectedGraph() 188 | self.assertRaises(IPySigmaGraphNodeIndexError, blank_graph.sigma_make_graph) 189 | 190 | 191 | def test_sigma_build_pandas_dfs_raise_edge_index_error_if_blank(self): 192 | 193 | blank_graph = InjectedGraph() 194 | blank_graph.add_nodes_from(['a','b','c']) 195 | self.assertRaises(IPySigmaGraphEdgeIndexError, blank_graph.sigma_make_graph) 196 | 197 | 198 | def test_sigma_build_pandas_dfs_raise_error_if_none(self): 199 | 200 | def sigma_build_pandas_dfs_none(): 201 | ''' 202 | this simulates a bogus override of the sigma_build_pandas_df method 203 | ''' 204 | return pd.DataFrame(None), pd.DataFrame(None) 205 | 206 | self.graph.sigma_build_pandas_dfs = sigma_build_pandas_dfs_none 207 | 208 | self.assertRaises(IPySigmaGraphDataFrameValueError, self.graph.sigma_make_graph) 209 | 210 | 211 | 212 | def test_export_json_flag_if_empty(self): 213 | 214 | def sigma_build_pandas_dfs_none(): 215 | ''' 216 | this simulates a bogus override of the sigma_build_pandas_df method 217 | ''' 218 | return pd.DataFrame(None), pd.DataFrame(None) 219 | 220 | self.graph.sigma_build_pandas_dfs = sigma_build_pandas_dfs_none 221 | self.graph.sigma_nodes, self.graph.sigma_edges = self.graph.sigma_build_pandas_dfs() 222 | 223 | j = self.graph.sigma_export_json() 224 | 225 | self.assertTrue(json.loads(j)['error'] == 'true') 226 | 227 | def test_export_json_flag_if_dfs_do_not_exist(self): 228 | 229 | 230 | j = self.graph.sigma_export_json() 231 | 232 | self.assertTrue(json.loads(j)['error'] == 'true') 233 | 234 | 235 | def test_sigma_color_picker_correctly_assigns_random_colors(self): 236 | 237 | self.graph_attr.sigma_make_graph() 238 | 239 | 240 | n1_value = self.graph_attr.sigma_nodes['color'][0] 241 | n2_value = self.graph_attr.sigma_nodes['color'][1] 242 | n3_value = self.graph_attr.sigma_nodes['color'][2] 243 | n4_value = self.graph_attr.sigma_nodes['color'][3] 244 | 245 | self.assertEqual(n1_value,n2_value) 246 | self.assertNotEqual(n3_value,n4_value) 247 | 248 | 249 | 250 | def test_sigma_node_add_color_raises_node_type_error(self): 251 | 252 | for node in self.graph_attr.nodes(data=True): 253 | del node[1]['node_type'] 254 | 255 | self.graph_attr.sigma_nodes, self.graph_attr.sigma_edges = self.graph_attr.sigma_build_pandas_dfs() 256 | 257 | with self.assertRaises(IPySigmaNodeTypeError): 258 | self.graph_attr.sigma_node_add_color_node_type() 259 | 260 | 261 | 262 | def test_sigma_node_add_label_raises_label_error(self): 263 | 264 | for node in self.graph_attr.nodes(data=True): 265 | del node[1]['label'] 266 | 267 | self.graph_attr.sigma_nodes, self.graph_attr.sigma_edges = self.graph_attr.sigma_build_pandas_dfs() 268 | 269 | with self.assertRaises(IPySigmaLabelError): 270 | self.graph_attr.sigma_node_add_label() 271 | 272 | 273 | def test_sigma_node_add_color_default_assigns_default_to_node_type(self): 274 | 275 | for node in self.graph_attr.nodes(data=True): 276 | del node[1]['node_type'] 277 | 278 | self.graph_attr.sigma_make_graph() 279 | 280 | nt = self.graph_attr.sigma_nodes['node_type'].tolist() 281 | correct = ['undefined node_type' for i in range(4)] 282 | 283 | self.assertEqual(nt, correct) 284 | 285 | 286 | 287 | def test_sigma_node_add_label_default_assigns_id_to_label(self): 288 | 289 | for node in self.graph_attr.nodes(data=True): 290 | del node[1]['label'] 291 | 292 | self.graph_attr.sigma_make_graph() 293 | 294 | id_col = self.graph_attr.sigma_nodes['id'].tolist() 295 | label_col = self.graph_attr.sigma_nodes['label'].tolist() 296 | 297 | self.assertEqual(id_col, label_col) 298 | 299 | -------------------------------------------------------------------------------- /ipysig_test.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Welcome to the Demo - v0.1.4\n", 8 | "This notebook provides a simulated workflow for using IPySigma with networkx.\n", 9 | "\n", 10 | "We only need to import one class, IPySig, the main controller object along with networkx.\n" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": {}, 17 | "outputs": [ 18 | { 19 | "name": "stdout", 20 | "output_type": "stream", 21 | "text": [ 22 | "INFO:root:Generating grammar tables from /usr/lib/python2.7/lib2to3/Grammar.txt\n", 23 | "INFO:root:Generating grammar tables from /usr/lib/python2.7/lib2to3/PatternGrammar.txt\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "# notebook to simulate a jupyter working environment\n", 29 | "# the python module gets imported here...\n", 30 | "from ipysig.core import IPySig\n", 31 | "import networkx as nx\n" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "To start, we only need to import the IPySig object from the ipysig.core package. This is a singleton controller class that manages our sigma.js visualization connections. The root directory of the node.js app is passed in on instantiation. \n", 39 | "\n", 40 | "If the demo notebook is being run inside the repo, this directory is simply './app', but in production this would change to wherever the user installed the node app on their system. \n", 41 | "\n", 42 | "If all goes well, IPySig will initialize the express server and print the PID number for the running process. \n", 43 | "\n", 44 | "On instantiation, the API also injects a set of custom methods onto the nx.Graph class which allows the controller to interface with the graph object and reshape the networkx object into a format suitable both for web socket transfer across the node server and readable by Sigma.js on the frontend.\n" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 2, 50 | "metadata": {}, 51 | "outputs": [ 52 | { 53 | "name": "stdout", 54 | "output_type": "stream", 55 | "text": [ 56 | "node ./app/index.js --baseUrl http://localhost:8888/ --token=6e968baaafc0c7e8116622cbe24d49fc90b3ca9502808dc5\n", 57 | "loading express... \n", 58 | "IPySig express has started... PID: 3613\n" 59 | ] 60 | } 61 | ], 62 | "source": [ 63 | "ctrl = IPySig('./app') # note this should point to the root folder of the express app" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "Let's make a network graph from the stars dataset. This was a custom search from the NASA ADS api and contains relationships between authors and journals for the keyword search \"stars\".\n", 71 | "\n", 72 | "To bring up the visualization tabs we pass in the object instance and a unique identifier to the controllers `connect` method. This key name is important because it binds the browser tab's socket id to a reference of the object.\n", 73 | "\n", 74 | "After executing each cell the visualization tab should open and by entering a `title name` for the graph pressing `Load Graph`, the graph visualization object should appear. This object reference is stored both in the IPySig controller instance and in the browser via a Loki.js datastore.\n", 75 | "\n", 76 | "In our current demo, v0.1.0, we can only store one graph at a time per session.\n", 77 | "\n", 78 | "### Loading the Stars Dataset\n" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "metadata": { 85 | "collapsed": true 86 | }, 87 | "outputs": [], 88 | "source": [ 89 | "import pickle\n", 90 | "import os\n", 91 | "\n", 92 | "pkl_g = os.path.abspath(os.path.join('.','IPySig_demo_data', 'stars_test_graph.pkl'))\n", 93 | "stars_graph = pickle.load(open(pkl_g, \"rb\" )) " 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "The stars_graph was saved earlier and loaded via pickle for convenience, but nx graphs can obviously be created on the fly, loaded from a database or csv or come into the notebook from any other data source.\n", 101 | "\n", 102 | "In order to get the most out of IPySigma, certain node and edge list attributes need to be added to the graph object. The sigma graph loader looks for a few key headers that the user needs to provide. The keyword \"label\" provides the visible label for each node and \"node_type\" is used to cataegorize each node and set color attributes.\n", 103 | "\n", 104 | "Note that the system will still work without these labels, however, the user will lose some of the nicest features of Sigma.js.\n" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "stars_graph.nodes(data=True)[:5]" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "stars_graph.edges(data=True)[:10]" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "### Connecting the graph\n", 130 | "\n", 131 | "We connect the graph to a new browser session by calling the \"connect\" method on the controller and passing in the object and a key-name for the graph object. \n", 132 | "\n", 133 | "This method call spins up a browser tab and binds the web socket to this graph instance.\n", 134 | "\n", 135 | "From this point, we can provide a graph name in the browser tab and hit the load_graph button. This sends a signal to IPySig to call the `export_graph_instance()` method and jsonify the result.\n", 136 | "\n", 137 | "This method call essentially packages our networkx object and adds attributes to the graph instance that is needed by Sigma.js. It also adds some centrality measures for free, along with any other user given attributes.\n", 138 | "\n", 139 | "The user can now explore the nodes of the graph freely in the browser tab and get node information by toggling a simple jquery based inspector. \n" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 4, 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "name": "stdout", 149 | "output_type": "stream", 150 | "text": [ 151 | "Node received: stars\n" 152 | ] 153 | } 154 | ], 155 | "source": [ 156 | "ctrl.connect(stars_graph,'stars')" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "Quite a bit of functionality for our IPySig controller and node application is still under development.\n", 164 | "\n", 165 | "Our current demo does not yet support disconnect/reconnect methods and auto-kill of the server process when the kernel is accidentally or unexpectedly shuts down. Also some care needs to be taken in the current version so that the controller has a chance to boot the express server before a browser tab and web socket connection is open. \n", 166 | "\n", 167 | "Future implementation will also include the ability to store multiple graphs in a single browser session tab, by storing names in an in memory datastore.\n", 168 | "\n", 169 | "To cleanly exit the application for now we need to kill the express process manually. " 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": { 176 | "collapsed": true 177 | }, 178 | "outputs": [], 179 | "source": [] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": { 184 | "collapsed": true 185 | }, 186 | "source": [ 187 | "### Just for fun a random graph...\n", 188 | "\n", 189 | "Connecting a new graph will spin it up in a new tab. Without adding labels and node_types to the graph, default values are used." 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 5, 195 | "metadata": {}, 196 | "outputs": [ 197 | { 198 | "name": "stdout", 199 | "output_type": "stream", 200 | "text": [ 201 | "Node received: newman_watts\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "\n", 207 | "G = nx.random_graphs.newman_watts_strogatz_graph(80,15,0.2)\n", 208 | "ctrl.connect(G, 'newman_watts')" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [ 217 | "ctrl.kill_express_process()" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": { 224 | "collapsed": true 225 | }, 226 | "outputs": [], 227 | "source": [] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": { 233 | "collapsed": true 234 | }, 235 | "outputs": [], 236 | "source": [] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "outputs": [], 245 | "source": [] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": { 251 | "collapsed": true 252 | }, 253 | "outputs": [], 254 | "source": [] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": null, 259 | "metadata": { 260 | "collapsed": true 261 | }, 262 | "outputs": [], 263 | "source": [] 264 | } 265 | ], 266 | "metadata": { 267 | "kernelspec": { 268 | "display_name": "Python 2", 269 | "language": "python", 270 | "name": "python2" 271 | }, 272 | "language_info": { 273 | "codemirror_mode": { 274 | "name": "ipython", 275 | "version": 2 276 | }, 277 | "file_extension": ".py", 278 | "mimetype": "text/x-python", 279 | "name": "python", 280 | "nbconvert_exporter": "python", 281 | "pygments_lexer": "ipython2", 282 | "version": "2.7.10" 283 | } 284 | }, 285 | "nbformat": 4, 286 | "nbformat_minor": 2 287 | } 288 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ads==0.12.3 2 | appdirs==1.4.3 3 | backports-abc==0.5 4 | backports.shutil-get-terminal-size==1.0.0 5 | bleach==2.0.0 6 | certifi==2017.1.23 7 | configparser==3.5.0 8 | cycler==0.10.0 9 | decorator==4.0.11 10 | entrypoints==0.2.2 11 | enum34==1.1.6 12 | funcsigs==1.0.2 13 | functools32==3.2.3.post2 14 | html5lib==0.999999999 15 | httpretty==0.8.10 16 | ipykernel==4.6.0 17 | ipython==5.3.0 18 | ipython-genutils==0.2.0 19 | ipywidgets==6.0.0 20 | Jinja2==2.9.6 21 | jsonschema==2.6.0 22 | jupyter==1.0.0 23 | jupyter-client==5.0.1 24 | jupyter-console==5.1.0 25 | jupyter-core==4.3.0 26 | MarkupSafe==1.0 27 | matplotlib==2.0.2 28 | mistune==0.7.4 29 | mock==2.0.0 30 | nbconvert==5.1.1 31 | nbformat==4.3.0 32 | networkx==1.11 33 | nose==1.3.7 34 | notebook==5.0.0 35 | numpy==1.12.1 36 | packaging==16.8 37 | pandas==0.19.2 38 | pandocfilters==1.4.1 39 | pathlib2==2.2.1 40 | pbr==3.1.1 41 | pexpect==4.2.1 42 | pickleshare==0.7.4 43 | prompt-toolkit==1.0.14 44 | ptyprocess==0.5.1 45 | Pygments==2.2.0 46 | pyparsing==2.2.0 47 | python-dateutil==2.6.0 48 | pytz==2017.2 49 | pyzmq==16.0.2 50 | qtconsole==4.3.0 51 | requests==2.13.0 52 | scandir==1.5 53 | simplegeneric==0.8.1 54 | singledispatch==3.4.0.3 55 | six==1.10.0 56 | socketIO-client==0.7.2 57 | subprocess32==3.2.7 58 | terminado==0.6 59 | testpath==0.3 60 | tornado==4.4.3 61 | traitlets==4.3.2 62 | wcwidth==0.1.7 63 | webencodings==0.5.1 64 | websocket-client==0.40.0 65 | Werkzeug==0.12.2 66 | widgetsnbextension==2.0.0 67 | -------------------------------------------------------------------------------- /scripts/install_hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GIT_DIR=$(git rev-parse --git-dir) 4 | 5 | echo "Installing pre-commit hooks..." 6 | # this command creates symlink to our pre-push script 7 | ln -s ../../scripts/pre_commit.sh $GIT_DIR/hooks/pre-commit 8 | echo "Done" -------------------------------------------------------------------------------- /scripts/pre_commit.sh: -------------------------------------------------------------------------------- 1 | echo "Running pre-commit hook" 2 | ./scripts/run_tests.sh 3 | 4 | # $? stores exit value of the last command 5 | if [ $? -ne 0 ]; then 6 | echo "Tests must pass before commit... exiting" 7 | exit 1 8 | fi -------------------------------------------------------------------------------- /scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # if any command inside script returns error, exit and return that error 4 | set -e 5 | 6 | # magic line to ensure that we're always inside the root of our application, 7 | # no matter from which directory we'll run script 8 | # thanks to it we can just enter `./scripts/run-tests.bash` 9 | cd "${0%/*}/.." 10 | 11 | nosetests # python nose --------------------------------------------------------------------------------