Skip to content

Instantly share code, notes, and snippets.

@mkapuza
Last active April 2, 2020 20:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mkapuza/6d2b3f1ec71780028fe1f8b051fedcd8 to your computer and use it in GitHub Desktop.
Save mkapuza/6d2b3f1ec71780028fe1f8b051fedcd8 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"# Generating PDF Reports Programmatically in Python Using API Data \n",
"\n",
"## Introduction\n",
"Welcome to AlliedCrowds's guide to generating reports programmatically using the [Capital Finder API](https://alliedcrowds.com/services/capital-finder#header). This guide is general by design, so it will be useful, whether you are a Capital Finder subscriber or not.\n",
"\n",
"If you are not already a Captial Finder subscriber and are intested in running this notebook with a Capital Finder API key, please email us at capital.finder (at) alliedcrowds.com and we will supply you with a temporary key.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"## Defining Query Parametes\n",
"After reading the API documentation, the first step when using any API is definiting your query parameters. In order to do so, you have to understand what the API offers and what you would like to use it for. \n",
"\n",
"For this exercise, I decided to generate a report on the agriculture sector for Asian Crowdfunding platforms. This choice is merely demonstrative, however, and readers following along can chose whatever query parameters they would like."
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"provider_types = ['CROWD'] # Crowdfunding\n",
"provider_sector = ['54c03238-3533-465c-8620-fed363c7e811'] # Agriculture\n",
"provider_countries = ['AF', 'AM', 'AZ', 'BD', 'BT', 'KH', 'GE', 'IN', 'ID', 'IR', \n",
" 'IQ', 'JO', 'KZ', 'KP', 'KG', 'LA', 'LB', 'MY', 'MV', 'MN', \n",
" 'MM', 'NP', 'PK', 'PH', 'LK', 'SY', 'TJ', 'TH', 'TL', 'TR', \n",
" 'TM', 'UZ', 'VN', 'YE'] # Every Developing Country in Asia"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"It may not be immediately obvious what our query parameters are from the above. This is because we use specific codes that our API understands in order to standardize all queries.\n",
"\n",
"Countries use the ISO two letter code standard. Sectors use a universally unique identifier (UUID); this is because we allow each user of our API to generate their own sectors and the codes must be unique. For the provider types, we have created our own ENUM-like codes. "
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"## Querying the API\n",
"Using the query parameters defined above, we are now going to query the Capital FInder API. I have defined our API's base url as well as a query string based on the API documentation. I have also created a generic helper method called `get_items` to reduce code duplication. \n",
"\n",
"*Note that the authorization code is left blank, this is intentional. If you would like to run this exercise yourself please contact us at capital.finder (at) alliedcrowds.com for free access to an API key.*"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"import json\n",
"import requests \n",
"\n",
"base_url = \"https://api.alliedcrowds.com/1.0/\"\n",
"\n",
"query = f\"\"\"provider/search?\n",
" provider_type={''.join(provider_types)}&\n",
" country={','.join(provider_countries)}&\n",
" sector={','.join(provider_sector)}\"\"\"\n",
"\n",
"authorization_code = \"\"\n",
"\n",
"def get_items(url):\n",
" response = requests.get(url, headers={'Authorization': f\"Basic {authorization_code}\"})\n",
" data = json.loads(response.text)\n",
" return data\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Next, we will use our helper method, `get_items`, to find all agriculture crowdfunding platforms in Asia. \n",
"\n",
"*Note that we use a while loop here in order to keep the code simple, but you can try using a recursive method as an exercise.*"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"data = get_items(base_url + query)\n",
"items = data['_items']\n",
"\n",
"while True:\n",
" if (not data['_links'].get('next')):\n",
" break\n",
" data = get_items(base_url + data['_links']['next']['href'])\n",
" items += data['_items']"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Let's see what the output looks like:"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"provider_id\": 218,\n",
" \"name\": \"Cropital\",\n",
" \"description\": \"\\\"Cropital is a globally recognized organisation whose development is being supported by institutions in the US, Netherlands, Malaysia and the Philippines. This is a social enterprise providing farmers access to scalable and sustainable financing.\\\"\",\n",
" \"logo\": \"https://cdn.filestackcontent.com/617kn81HQzGBnJ2bXFM6\",\n",
" \"url\": \"https://www.cropital.com/\",\n",
" \"year_founded\": 2014,\n",
" \"_links\": {\n",
" \"self\": {\n",
" \"title\": \"Provider_search\",\n",
" \"href\": \"provider/218\"\n",
" }\n",
" }\n",
"}\n"
]
}
],
"source": [
"print(json.dumps(items[0], indent=2))"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Everything looks good, but we will need more data if we would like to publish a report on these providers. Therefore, we must query the endpoint provided with each capital provider (shown below) in order to find out more from the Capital Finder API.\n",
"```\n",
"\"_links\": {\n",
" \"self\": {\n",
" \"title\": \"Provider_search\",\n",
" \"href\": \"provider/218\"\n",
" }\n",
"}\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"## Going Deeper\n",
"In order to find out more about each provider, we query the `/provider` endpoint for each provider in our search results. To do this, we will iterate through our `items` list, grab the endpoint and query the API, along with an \"embed\" string, that tells the API what additional information we require.\n",
"\n",
"Finally, we will store this information in a new array called `providers`.\n",
"\n",
"*Note that if you are trying this at home, it may take a minute, since we are executing a lot of API calls.* "
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"providers = []\n",
"\n",
"for item in items:\n",
" embedded = '?embedded={\"provider_capital_types\":1,\"provider_capital_types.capital_type\":1,\"provider_countries\":1,\"provider_countries.country\":1,\"contacts\":1,\"contacts.office\":1,\"provider_currencies\":1,\"provider_currencies.currency\":1,\"provider_languages\":1,\"provider_languages.language\":1,\"offices\":1,\"offices.country\":1,\"provider_sectors\":1,\"provider_sectors.sector\":1,\"provider_social_medias\":1,\"provider_social_medias.social_media\":1,\"provider_provider_types\":1,\"provider_provider_types.provider_type\":1,\"provider_stats\":1,\"provider_stats.stat\":1}'\n",
" data = get_items(base_url + item['_links']['self']['href'] + embedded)\n",
" providers.append(data)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Let's see what our updated data looks like. This output will be very long, as we have a lot of data on each provider:"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"id\": 218,\n",
" \"name\": \"Cropital\",\n",
" \"description\": \"\\\"Cropital is a globally recognized organisation whose development is being supported by institutions in the US, Netherlands, Malaysia and the Philippines. This is a social enterprise providing farmers access to scalable and sustainable financing.\\\"\",\n",
" \"url\": \"https://www.cropital.com/\",\n",
" \"year_founded\": 2014,\n",
" \"logo\": \"https://cdn.filestackcontent.com/617kn81HQzGBnJ2bXFM6\",\n",
" \"cover_photo\": \"https://cdn.filestackcontent.com/ktI1hUoTmiJQKcykMxEB\",\n",
" \"provider_countries\": [\n",
" {\n",
" \"id\": 2139,\n",
" \"object_id\": 218,\n",
" \"country_id\": 24,\n",
" \"relevancy\": 0.0783,\n",
" \"facebook_likes\": 9.0,\n",
" \"country\": {\n",
" \"code\": \"BR\",\n",
" \"developing\": true,\n",
" \"name\": \"Brazil\",\n",
" \"id\": 24,\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"...\"\n",
" {\n",
" \"id\": 91178,\n",
" \"object_id\": 218,\n",
" \"country_id\": 140,\n",
" \"relevancy\": 0.2196,\n",
" \"facebook_likes\": 167.0,\n",
" \"country\": {\n",
" \"code\": \"QA\",\n",
" \"developing\": false,\n",
" \"name\": \"Qatar\",\n",
" \"id\": 140,\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" }\n",
" ],\n",
" \"provider_sectors\": [\n",
" {\n",
" \"id\": 64324,\n",
" \"object_id\": 218,\n",
" \"sector_id\": 27,\n",
" \"relevancy\": 0.24552583362683444,\n",
" \"sector\": {\n",
" \"code\": \"54c03238-3533-465c-8620-fed363c7e811\",\n",
" \"name\": \"Agriculture\",\n",
" \"id\": 27,\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:15 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:11 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:16 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:12 GMT\"\n",
" },\n",
" \"...\"\n",
" {\n",
" \"id\": 64327,\n",
" \"object_id\": 218,\n",
" \"sector_id\": 30,\n",
" \"relevancy\": 0.010222243948325344,\n",
" \"sector\": {\n",
" \"code\": \"c951090d-ea59-4f60-95d6-dc89d66e9171\",\n",
" \"name\": \"Technology\",\n",
" \"id\": 30,\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:15 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:11 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:16 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:12 GMT\"\n",
" }\n",
" ],\n",
" \"provider_capital_types\": [\n",
" {\n",
" \"id\": 6225,\n",
" \"object_id\": 218,\n",
" \"capital_type_id\": 1,\n",
" \"capital_type\": {\n",
" \"name\": \"Equity\",\n",
" \"code\": \"EQUITY\",\n",
" \"id\": 1,\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:17 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:15 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:11 GMT\"\n",
" }\n",
" ],\n",
" \"provider_provider_types\": [\n",
" {\n",
" \"id\": 11062,\n",
" \"object_id\": 218,\n",
" \"provider_type_id\": 13,\n",
" \"provider_type\": {\n",
" \"name\": \"Crowdfunding\",\n",
" \"code\": \"CROWD\",\n",
" \"id\": 13,\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:16 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:12 GMT\"\n",
" }\n",
" ],\n",
" \"provider_languages\": [\n",
" {\n",
" \"id\": 267,\n",
" \"object_id\": 218,\n",
" \"language_id\": 41,\n",
" \"language\": {\n",
" \"id\": 41,\n",
" \"code\": \"en\",\n",
" \"name\": \"English\",\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:16 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:12 GMT\"\n",
" }\n",
" ],\n",
" \"provider_currencies\": [\n",
" {\n",
" \"id\": 13503,\n",
" \"object_id\": 218,\n",
" \"currency_id\": 121,\n",
" \"currency\": {\n",
" \"id\": 121,\n",
" \"code\": \"PHP\",\n",
" \"name\": \"Philippine Peso\",\n",
" \"symbol\": \"\\u20b1\",\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:16 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:12 GMT\"\n",
" }\n",
" ],\n",
" \"provider_social_medias\": [\n",
" {\n",
" \"id\": 8161,\n",
" \"object_id\": 218,\n",
" \"social_media_id\": 1,\n",
" \"url\": \"https://www.facebook.com/Cropital/\",\n",
" \"social_media\": {\n",
" \"id\": 1,\n",
" \"url\": \"https://www.facebook.com/\",\n",
" \"name\": \"Facebook\",\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:13 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:10 GMT\"\n",
" },\n",
" \"updated_at\": \"Wed, 11 Apr 2018 09:01:15 GMT\",\n",
" \"created_at\": \"Wed, 11 Apr 2018 09:01:11 GMT\"\n",
" }\n",
" ],\n",
" \"provider_stats\": [],\n",
" \"contacts\": [],\n",
" \"offices\": [],\n",
" \"_links\": {\n",
" \"parent\": {\n",
" \"title\": \"home\",\n",
" \"href\": \"/\"\n",
" },\n",
" \"self\": {\n",
" \"title\": \"Provider\",\n",
" \"href\": \"provider/218\"\n",
" },\n",
" \"collection\": {\n",
" \"title\": \"provider\",\n",
" \"href\": \"provider\"\n",
" }\n",
" }\n",
"}\n"
]
}
],
"source": [
"print(json.dumps(providers[0], indent=2))"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"This data is much better! We will definitely be able to create a useful directory with this. The only problem remaining is going to be getting the data in a format that works for exporting to a report."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"## Processing the Output\n",
"The new data has a lot of \"metadata\" or data used to describe data. This metadata is useful for many different applications, but in this case, much of it is superfluous. For example, instead of a list of country names that this capital provider is active in, we get a list of `provider_countries` with the [Relevancy Score](https://blog.alliedcrowds.com/capital-relevancy-score/) (a score that represents the likelihood of a capital provider funding a company or project in a given country), the number of Facebook likes that provider has in the country, the country code, when that record was last created, when it was updated, and more. We do not need all of this. \n",
"\n",
"What we want in the case of this directory is simply a list of all the country names above a certain relevancy threshold; in our case, it is `0.1`, because any country with a score between `0.1` and `0.9` is considered \"semi-active\", based on our methodology. You can see in the table on page 3 in the example directory that some countries are represented as semi-circles, these are the semi-active countries. In our directories, when a country has a score higher than `0.9`, that country is considered active for that capital provider and is designated with a full circle. Below the `0.1` threshold, the provider is not considered to be active in that country.\n",
"\n",
"Below we iterate through the data for each provider, and simply select only the data that is important for this report."
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"id\": 218,\n",
" \"name\": \"Cropital\",\n",
" \"description\": \"\\\"Cropital is a globally recognized organisation whose development is being supported by institutions in the US, Netherlands, Malaysia and the Philippines. This is a social enterprise providing farmers access to scalable and sustainable financing.\\\"\",\n",
" \"url\": \"https://www.cropital.com/\",\n",
" \"year_founded\": 2014,\n",
" \"logo\": \"https://cdn.filestackcontent.com/617kn81HQzGBnJ2bXFM6\",\n",
" \"cover_photo\": \"https://cdn.filestackcontent.com/ktI1hUoTmiJQKcykMxEB\",\n",
" \"provider_countries\": [\n",
" \"Indonesia\",\n",
" \"India\",\n",
" \"Papua New Guinea\",\n",
" \"Philippines\",\n",
" \"Thailand\",\n",
" \"Vietnam\",\n",
" \"Malaysia\"\n",
" ],\n",
" \"provider_sectors\": [\n",
" \"Agriculture\",\n",
" \"Education\",\n",
" \"Renewables\",\n",
" \"Technology\"\n",
" ],\n",
" \"provider_capital_types\": [\n",
" \"Equity\"\n",
" ],\n",
" \"provider_provider_types\": [\n",
" \"Crowdfunding\"\n",
" ],\n",
" \"provider_languages\": [\n",
" \"English\"\n",
" ],\n",
" \"provider_currencies\": [\n",
" \"Philippine Peso\"\n",
" ],\n",
" \"provider_social_medias\": [\n",
" \"https://www.facebook.com/Cropital/\"\n",
" ],\n",
" \"provider_stats\": [],\n",
" \"contacts\": [],\n",
" \"offices\": [],\n",
" \"_links\": {\n",
" \"parent\": {\n",
" \"title\": \"home\",\n",
" \"href\": \"/\"\n",
" },\n",
" \"self\": {\n",
" \"title\": \"Provider\",\n",
" \"href\": \"provider/218\"\n",
" },\n",
" \"collection\": {\n",
" \"title\": \"provider\",\n",
" \"href\": \"provider\"\n",
" }\n",
" },\n",
" \"provider_half_countries\": [\n",
" \"Indonesia\",\n",
" \"India\",\n",
" \"Papua New Guinea\",\n",
" \"Thailand\",\n",
" \"Vietnam\",\n",
" \"Malaysia\"\n",
" ],\n",
" \"provider_full_countries\": [\n",
" \"Philippines\"\n",
" ]\n",
"}\n"
]
}
],
"source": [
"for provider in providers:\n",
" for k, v in provider.items():\n",
" if (k == 'provider_capital_types'):\n",
" provider[k] = [capital_type['capital_type']['name'] for capital_type in v]\n",
" \n",
" elif (k == 'provider_countries'):\n",
" provider[k] = [country['country']['name'] for country in v if country['relevancy'] > .1 and country['country']['developing'] == True] \n",
" provider_half_countries = [country['country']['name'] for country in v if country['relevancy'] > .1 and country['relevancy'] < .9 and country['country']['developing'] == True]\n",
" provider_full_countries = [country['country']['name'] for country in v if country['relevancy'] > .9 and country['country']['developing'] == True]\n",
" \n",
" elif (k == 'provider_languages'):\n",
" provider[k] = [language['language']['name'] for language in v]\n",
" \n",
" elif (k == 'provider_currencies'):\n",
" provider[k] = [currency['currency']['name'] for currency in v]\n",
" \n",
" elif (k == 'provider_provider_types'):\n",
" provider[k] = [provider_type['provider_type']['name'] for provider_type in v if provider_type.get('provider_type')]\n",
" \n",
" elif (k == 'provider_sectors'):\n",
" provider[k] = [sector['sector']['name'] for sector in v if sector.get('sector')]\n",
" \n",
" elif (k == 'provider_social_medias'):\n",
" provider[k] = [social_media['url'] for social_media in v]\n",
" \n",
" provider['provider_half_countries'] = provider_half_countries\n",
" provider['provider_full_countries'] = provider_full_countries\n",
" \n",
"print(json.dumps(providers[0], indent=2))\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"## Using DataFrames\n",
"Once we have our data processed, we can put it inside of a dataframe for a more \"human-readable\" format. Dataframes are a lot like excel tables and we can also do some manipulations and transformations to the dataframe that we can't do with a typical Python dictionary. We will also select the subset of columns that we would like to use in our report to clean things up a bit. "
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>name</th>\n",
" <th>url</th>\n",
" <th>year_founded</th>\n",
" <th>description</th>\n",
" <th>provider_provider_types</th>\n",
" <th>provider_sectors</th>\n",
" <th>provider_languages</th>\n",
" <th>provider_social_medias</th>\n",
" <th>provider_capital_types</th>\n",
" <th>provider_currencies</th>\n",
" <th>provider_countries</th>\n",
" <th>provider_full_countries</th>\n",
" <th>provider_half_countries</th>\n",
" <th>offices</th>\n",
" <th>contacts</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Cropital</td>\n",
" <td>https://www.cropital.com/</td>\n",
" <td>2014</td>\n",
" <td>\"Cropital is a globally recognized organisatio...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Education, Renewables, Technology]</td>\n",
" <td>[English]</td>\n",
" <td>[https://www.facebook.com/Cropital/]</td>\n",
" <td>[Equity]</td>\n",
" <td>[Philippine Peso]</td>\n",
" <td>[Indonesia, India, Papua New Guinea, Philippin...</td>\n",
" <td>[Philippines]</td>\n",
" <td>[Indonesia, India, Papua New Guinea, Thailand,...</td>\n",
" <td>[]</td>\n",
" <td>[]</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>KICKSTARTER</td>\n",
" <td>https://www.kickstarter.com/</td>\n",
" <td>2009</td>\n",
" <td>\"Kickstarter helps artists, musicians, filmmak...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Renewables, Technology, Industri...</td>\n",
" <td>[English]</td>\n",
" <td>[]</td>\n",
" <td>[Grant]</td>\n",
" <td>[US Dollar]</td>\n",
" <td>[Thailand, Philippines, Malaysia, Indonesia, I...</td>\n",
" <td>[]</td>\n",
" <td>[Thailand, Philippines, Malaysia, Indonesia, I...</td>\n",
" <td>[{'id': 7609, 'address_line_1': None, 'address...</td>\n",
" <td>[]</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Ketto</td>\n",
" <td>https://www.ketto.org/</td>\n",
" <td>2012</td>\n",
" <td>\"Ketto is a crowdfunding platform for social, ...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Education, Renewables, Technolog...</td>\n",
" <td>[English]</td>\n",
" <td>[]</td>\n",
" <td>[Grant]</td>\n",
" <td>[Indian Rupee]</td>\n",
" <td>[India]</td>\n",
" <td>[India]</td>\n",
" <td>[]</td>\n",
" <td>[{'id': 7597, 'address_line_1': '1302-1306, 13...</td>\n",
" <td>[]</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>MEKAR Network, PT. Sampoerna Wirausaha</td>\n",
" <td>https://mekar.id</td>\n",
" <td>2010</td>\n",
" <td>\"Mekar adalah perusahaan pembiayaan inovatif y...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Renewables, Technology]</td>\n",
" <td>[English, Indonesian]</td>\n",
" <td>[https://twitter.com/mekarnetwork, https://www...</td>\n",
" <td>[Lending]</td>\n",
" <td>[Rupiah]</td>\n",
" <td>[Indonesia, Bangladesh, Malaysia]</td>\n",
" <td>[Indonesia]</td>\n",
" <td>[Bangladesh, Malaysia]</td>\n",
" <td>[{'id': 5804, 'address_line_1': 'Jl. Brawijaya...</td>\n",
" <td>[]</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>CrowdPlus.Asia</td>\n",
" <td>http://www.crowdplus.asia/</td>\n",
" <td>2015</td>\n",
" <td>\"CrowdPlus.asia is an equity crowdfunding plat...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Education, Renewables, Technolog...</td>\n",
" <td>[English]</td>\n",
" <td>[https://twitter.com/crowdplus_asia, https://w...</td>\n",
" <td>[Equity]</td>\n",
" <td>[]</td>\n",
" <td>[Indonesia, India, Cambodia, Myanmar, Nepal, P...</td>\n",
" <td>[Malaysia]</td>\n",
" <td>[Indonesia, India, Cambodia, Myanmar, Nepal, P...</td>\n",
" <td>[{'id': 6251, 'address_line_1': 'No 12, Omni K...</td>\n",
" <td>[]</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" name url \\\n",
"0 Cropital https://www.cropital.com/ \n",
"1 KICKSTARTER https://www.kickstarter.com/ \n",
"2 Ketto https://www.ketto.org/ \n",
"3 MEKAR Network, PT. Sampoerna Wirausaha https://mekar.id \n",
"4 CrowdPlus.Asia http://www.crowdplus.asia/ \n",
"\n",
" year_founded description \\\n",
"0 2014 \"Cropital is a globally recognized organisatio... \n",
"1 2009 \"Kickstarter helps artists, musicians, filmmak... \n",
"2 2012 \"Ketto is a crowdfunding platform for social, ... \n",
"3 2010 \"Mekar adalah perusahaan pembiayaan inovatif y... \n",
"4 2015 \"CrowdPlus.asia is an equity crowdfunding plat... \n",
"\n",
" provider_provider_types provider_sectors \\\n",
"0 [Crowdfunding] [Agriculture, Education, Renewables, Technology] \n",
"1 [Crowdfunding] [Agriculture, Renewables, Technology, Industri... \n",
"2 [Crowdfunding] [Agriculture, Education, Renewables, Technolog... \n",
"3 [Crowdfunding] [Agriculture, Renewables, Technology] \n",
"4 [Crowdfunding] [Agriculture, Education, Renewables, Technolog... \n",
"\n",
" provider_languages provider_social_medias \\\n",
"0 [English] [https://www.facebook.com/Cropital/] \n",
"1 [English] [] \n",
"2 [English] [] \n",
"3 [English, Indonesian] [https://twitter.com/mekarnetwork, https://www... \n",
"4 [English] [https://twitter.com/crowdplus_asia, https://w... \n",
"\n",
" provider_capital_types provider_currencies \\\n",
"0 [Equity] [Philippine Peso] \n",
"1 [Grant] [US Dollar] \n",
"2 [Grant] [Indian Rupee] \n",
"3 [Lending] [Rupiah] \n",
"4 [Equity] [] \n",
"\n",
" provider_countries provider_full_countries \\\n",
"0 [Indonesia, India, Papua New Guinea, Philippin... [Philippines] \n",
"1 [Thailand, Philippines, Malaysia, Indonesia, I... [] \n",
"2 [India] [India] \n",
"3 [Indonesia, Bangladesh, Malaysia] [Indonesia] \n",
"4 [Indonesia, India, Cambodia, Myanmar, Nepal, P... [Malaysia] \n",
"\n",
" provider_half_countries \\\n",
"0 [Indonesia, India, Papua New Guinea, Thailand,... \n",
"1 [Thailand, Philippines, Malaysia, Indonesia, I... \n",
"2 [] \n",
"3 [Bangladesh, Malaysia] \n",
"4 [Indonesia, India, Cambodia, Myanmar, Nepal, P... \n",
"\n",
" offices contacts \n",
"0 [] [] \n",
"1 [{'id': 7609, 'address_line_1': None, 'address... [] \n",
"2 [{'id': 7597, 'address_line_1': '1302-1306, 13... [] \n",
"3 [{'id': 5804, 'address_line_1': 'Jl. Brawijaya... [] \n",
"4 [{'id': 6251, 'address_line_1': 'No 12, Omni K... [] "
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"\n",
"df = pd.DataFrame(providers)\n",
"df = df[[\"name\", \"url\", \"year_founded\", \"description\", \"provider_provider_types\", \"provider_sectors\", \"provider_languages\", \"provider_social_medias\", \"provider_capital_types\", \"provider_currencies\", \"provider_countries\", \"provider_full_countries\", \"provider_half_countries\", \"offices\", \"contacts\"]]\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"One example of making use of dataframes is counting the most popular countries. The following code iterates through our countries, uses the `get_items` method to find the corresponding country name from the Capital Finder API, and then counts how many times that country name appears in the dataframe."
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>country</th>\n",
" <th>code</th>\n",
" <th>count</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>India</td>\n",
" <td>IN</td>\n",
" <td>43</td>\n",
" </tr>\n",
" <tr>\n",
" <th>17</th>\n",
" <td>Malaysia</td>\n",
" <td>MY</td>\n",
" <td>34</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>Indonesia</td>\n",
" <td>ID</td>\n",
" <td>34</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Bangladesh</td>\n",
" <td>BD</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>23</th>\n",
" <td>Philippines</td>\n",
" <td>PH</td>\n",
" <td>20</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" country code count\n",
"7 India IN 43\n",
"17 Malaysia MY 34\n",
"8 Indonesia ID 34\n",
"3 Bangladesh BD 21\n",
"23 Philippines PH 20"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"countries_df = pd.DataFrame(columns=['country', 'code', 'count'])\n",
"\n",
"for code in provider_countries:\n",
" query = f\"country/{code}\"\n",
" country = get_items(base_url + query)\n",
" tdf = df[df[\"provider_countries\"].astype(str).str.contains(country['name'], na=False)]\n",
" countries_df = countries_df.append({'country': country['name'], 'code': code, 'count': tdf.shape[0]}, ignore_index=True)\n",
"\n",
"countries_df = countries_df.sort_values(by='count', ascending=False)\n",
"countries_df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Let's put this information into an ordered dictionary and call it `league_table`. We will also select the top countries from this list and store it in a list called `top_countries`."
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"data": {
"text/plain": [
"['India',\n",
" 'Malaysia',\n",
" 'Indonesia',\n",
" 'Bangladesh',\n",
" 'Philippines',\n",
" 'Vietnam',\n",
" 'Thailand',\n",
" 'Pakistan',\n",
" 'Turkey',\n",
" 'Cambodia',\n",
" 'Jordan',\n",
" 'Nepal',\n",
" 'Lebanon',\n",
" 'Afghanistan']"
]
},
"execution_count": 69,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import collections\n",
"\n",
"league_table = countries_df.to_dict(orient=\"index\")\n",
"league_table = dict((value['country'], value['count']) for key, value in league_table.items())\n",
"league_table = collections.OrderedDict(league_table)\n",
"\n",
"top_countries = countries_df.country.tolist()[:14]\n",
"top_countries"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"We want to make a table that shows each of the capital providers within the top countries. We also want to show an \"other\" column for the reader to determine if the capital provider is active in another countries. To do so, we want to measure if the there are any \"full\" or \"half\" countries for the provider that are not in the `top_countries` list; we use [set subtraction][1] to calculate these. See the \"Other\" column in figure 1 above.\n",
"\n",
"[1]:https://en.wikipedia.org/wiki/Complement_(set_theory)"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>name</th>\n",
" <th>url</th>\n",
" <th>year_founded</th>\n",
" <th>description</th>\n",
" <th>provider_provider_types</th>\n",
" <th>provider_sectors</th>\n",
" <th>provider_languages</th>\n",
" <th>provider_social_medias</th>\n",
" <th>provider_capital_types</th>\n",
" <th>provider_currencies</th>\n",
" <th>provider_countries</th>\n",
" <th>provider_full_countries</th>\n",
" <th>provider_half_countries</th>\n",
" <th>offices</th>\n",
" <th>contacts</th>\n",
" <th>other</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Cropital</td>\n",
" <td>https://www.cropital.com/</td>\n",
" <td>2014</td>\n",
" <td>\"Cropital is a globally recognized organisatio...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Education, Renewables, Technology]</td>\n",
" <td>[English]</td>\n",
" <td>[https://www.facebook.com/Cropital/]</td>\n",
" <td>[Equity]</td>\n",
" <td>[Philippine Peso]</td>\n",
" <td>[Indonesia, India, Papua New Guinea, Philippin...</td>\n",
" <td>[Philippines]</td>\n",
" <td>[Indonesia, India, Papua New Guinea, Thailand,...</td>\n",
" <td>[]</td>\n",
" <td>[]</td>\n",
" <td>half</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>KICKSTARTER</td>\n",
" <td>https://www.kickstarter.com/</td>\n",
" <td>2009</td>\n",
" <td>\"Kickstarter helps artists, musicians, filmmak...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Renewables, Technology, Industri...</td>\n",
" <td>[English]</td>\n",
" <td>[]</td>\n",
" <td>[Grant]</td>\n",
" <td>[US Dollar]</td>\n",
" <td>[Thailand, Philippines, Malaysia, Indonesia, I...</td>\n",
" <td>[]</td>\n",
" <td>[Thailand, Philippines, Malaysia, Indonesia, I...</td>\n",
" <td>[{'id': 7609, 'address_line_1': None, 'address...</td>\n",
" <td>[]</td>\n",
" <td>none</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Ketto</td>\n",
" <td>https://www.ketto.org/</td>\n",
" <td>2012</td>\n",
" <td>\"Ketto is a crowdfunding platform for social, ...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Education, Renewables, Technolog...</td>\n",
" <td>[English]</td>\n",
" <td>[]</td>\n",
" <td>[Grant]</td>\n",
" <td>[Indian Rupee]</td>\n",
" <td>[India]</td>\n",
" <td>[India]</td>\n",
" <td>[]</td>\n",
" <td>[{'id': 7597, 'address_line_1': '1302-1306, 13...</td>\n",
" <td>[]</td>\n",
" <td>none</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>MEKAR Network, PT. Sampoerna Wirausaha</td>\n",
" <td>https://mekar.id</td>\n",
" <td>2010</td>\n",
" <td>\"Mekar adalah perusahaan pembiayaan inovatif y...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Renewables, Technology]</td>\n",
" <td>[English, Indonesian]</td>\n",
" <td>[https://twitter.com/mekarnetwork, https://www...</td>\n",
" <td>[Lending]</td>\n",
" <td>[Rupiah]</td>\n",
" <td>[Indonesia, Bangladesh, Malaysia]</td>\n",
" <td>[Indonesia]</td>\n",
" <td>[Bangladesh, Malaysia]</td>\n",
" <td>[{'id': 5804, 'address_line_1': 'Jl. Brawijaya...</td>\n",
" <td>[]</td>\n",
" <td>none</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>CrowdPlus.Asia</td>\n",
" <td>http://www.crowdplus.asia/</td>\n",
" <td>2015</td>\n",
" <td>\"CrowdPlus.asia is an equity crowdfunding plat...</td>\n",
" <td>[Crowdfunding]</td>\n",
" <td>[Agriculture, Education, Renewables, Technolog...</td>\n",
" <td>[English]</td>\n",
" <td>[https://twitter.com/crowdplus_asia, https://w...</td>\n",
" <td>[Equity]</td>\n",
" <td>[]</td>\n",
" <td>[Indonesia, India, Cambodia, Myanmar, Nepal, P...</td>\n",
" <td>[Malaysia]</td>\n",
" <td>[Indonesia, India, Cambodia, Myanmar, Nepal, P...</td>\n",
" <td>[{'id': 6251, 'address_line_1': 'No 12, Omni K...</td>\n",
" <td>[]</td>\n",
" <td>half</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" name url \\\n",
"0 Cropital https://www.cropital.com/ \n",
"1 KICKSTARTER https://www.kickstarter.com/ \n",
"2 Ketto https://www.ketto.org/ \n",
"3 MEKAR Network, PT. Sampoerna Wirausaha https://mekar.id \n",
"4 CrowdPlus.Asia http://www.crowdplus.asia/ \n",
"\n",
" year_founded description \\\n",
"0 2014 \"Cropital is a globally recognized organisatio... \n",
"1 2009 \"Kickstarter helps artists, musicians, filmmak... \n",
"2 2012 \"Ketto is a crowdfunding platform for social, ... \n",
"3 2010 \"Mekar adalah perusahaan pembiayaan inovatif y... \n",
"4 2015 \"CrowdPlus.asia is an equity crowdfunding plat... \n",
"\n",
" provider_provider_types provider_sectors \\\n",
"0 [Crowdfunding] [Agriculture, Education, Renewables, Technology] \n",
"1 [Crowdfunding] [Agriculture, Renewables, Technology, Industri... \n",
"2 [Crowdfunding] [Agriculture, Education, Renewables, Technolog... \n",
"3 [Crowdfunding] [Agriculture, Renewables, Technology] \n",
"4 [Crowdfunding] [Agriculture, Education, Renewables, Technolog... \n",
"\n",
" provider_languages provider_social_medias \\\n",
"0 [English] [https://www.facebook.com/Cropital/] \n",
"1 [English] [] \n",
"2 [English] [] \n",
"3 [English, Indonesian] [https://twitter.com/mekarnetwork, https://www... \n",
"4 [English] [https://twitter.com/crowdplus_asia, https://w... \n",
"\n",
" provider_capital_types provider_currencies \\\n",
"0 [Equity] [Philippine Peso] \n",
"1 [Grant] [US Dollar] \n",
"2 [Grant] [Indian Rupee] \n",
"3 [Lending] [Rupiah] \n",
"4 [Equity] [] \n",
"\n",
" provider_countries provider_full_countries \\\n",
"0 [Indonesia, India, Papua New Guinea, Philippin... [Philippines] \n",
"1 [Thailand, Philippines, Malaysia, Indonesia, I... [] \n",
"2 [India] [India] \n",
"3 [Indonesia, Bangladesh, Malaysia] [Indonesia] \n",
"4 [Indonesia, India, Cambodia, Myanmar, Nepal, P... [Malaysia] \n",
"\n",
" provider_half_countries \\\n",
"0 [Indonesia, India, Papua New Guinea, Thailand,... \n",
"1 [Thailand, Philippines, Malaysia, Indonesia, I... \n",
"2 [] \n",
"3 [Bangladesh, Malaysia] \n",
"4 [Indonesia, India, Cambodia, Myanmar, Nepal, P... \n",
"\n",
" offices contacts other \n",
"0 [] [] half \n",
"1 [{'id': 7609, 'address_line_1': None, 'address... [] none \n",
"2 [{'id': 7597, 'address_line_1': '1302-1306, 13... [] none \n",
"3 [{'id': 5804, 'address_line_1': 'Jl. Brawijaya... [] none \n",
"4 [{'id': 6251, 'address_line_1': 'No 12, Omni K... [] half "
]
},
"execution_count": 70,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def other_countries(provider):\n",
" if len(set(provider['provider_full_countries']) - set(top_countries)) > 0:\n",
" return \"full\" \n",
" elif len(set(provider['provider_half_countries']) - set(top_countries)) > 0:\n",
" return \"half\" \n",
" else: \n",
" return \"none\"\n",
" \n",
"df['other'] = df.apply(other_countries, axis=1) \n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Finally, we have everything that we need in order to generate our report. We will use `jinja2` as the templating engine. `jinja2` allows us to use special syntax within an HTML file to lay out the report using HTML and CSS. You can see an example of the template that we use attached to the blog.\n",
"\n",
"*Note that if you are running this at home, save the HTML file from the blog as `report_template.html` in a directory called `resources` in the same directory as the Jupyter Notebook.*"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"from jinja2 import Environment, FileSystemLoader\n",
"import os\n",
"\n",
"env = Environment(loader=FileSystemLoader(os.getcwd() + \"/resources/\"), extensions=['jinja2.ext.loopcontrols'])\n",
"template = env.get_template(\"report_template.html\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Once we have our template completed, we load our variables into it. These template variables are simply variables that we would like to use within the template. "
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"import datetime\n",
"providers = df.sort_values(by='name').to_dict(orient='records')\n",
"\n",
"template_vars = { \"title\" : \"Agriculture Crowdfunding Report in Asia\",\n",
" \"date\": f\"{datetime.datetime.now().strftime('%B')} {datetime.datetime.now().strftime('%Y')}\",\n",
" \"providers\" : providers,\n",
" \"top_countries\" : top_countries,\n",
" \"league_table\" : league_table,\n",
" }\n",
"\n",
"html_out = template.render(template_vars)"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Finally we use `weasyprint`'s `HTML` class to generate the report."
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"from weasyprint import HTML\n",
"\n",
"pdf = HTML(string=html_out, base_url=os.getcwd() + \"/resources/\").write_pdf(\"report.pdf\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment