Last active
October 29, 2023 17:43
-
-
Save gunessenturk/51694cf08892691f76472cb5745bb285 to your computer and use it in GitHub Desktop.
Classical_events
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import requests\n", | |
"import dill\n", | |
"from bs4 import BeautifulSoup\n", | |
"from datetime import datetime\n", | |
"import re\n", | |
"import pandas as pd\n", | |
"import datetime as dt\n", | |
"import unicodedata\n", | |
"import numpy as np" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from sklearn import base\n", | |
"from sklearn.feature_extraction import DictVectorizer\n", | |
"from sklearn.pipeline import Pipeline, FeatureUnion\n", | |
"from sklearn.neighbors import NearestNeighbors" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Classical music event guide" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"These two dictionaries are used for tagging individual events in preparation for the recommendation engine." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"composer_nationality = {'hindemith': 'german', 'debussy': 'french', 'mozart': 'austrian', 'liszt': 'hungarian',\n", | |
" 'schumann': 'german', 'faure':'french', 'ravel': 'french', 'saint-saens': 'french', \n", | |
" 'saint-saans': 'french', 'rheinberger': 'german', 'korngold': 'austrian',\n", | |
" 'tchaikovsky': 'russian', 'brahms': 'german', 'boccherini': 'italian', 'messiaen': 'french', \n", | |
" 'barber': 'american', 'rachmaninoff': 'russian', 'schubert': 'austrian', \n", | |
" 'schoenberg': 'austrian', 'mendelssohn': 'german', 'beethoven': 'german', 'prokofiev':'russian', \n", | |
" 'shostakovich':'russian', 'khachaturian': 'russian', 'britten': 'english', \n", | |
" 'mahler': 'austrian', 'wagner': 'german', 'berlioz': 'french', 'roussel': 'french', \n", | |
" 'haydn': 'austrian', 'bruckner': 'austrian', 'franck': 'french', 'rossini': 'italian', \n", | |
" 'copland': 'american', 'stravinsky': 'russian', 'bates': 'american', 'muhly': 'american', \n", | |
" 'nesbett': 'english', 'byrd': 'english', 'praetorius': 'german', 'bartók': 'hungarian', \n", | |
" 'bartok': 'hungarian', 'rzewski': 'american', 'loeffler': 'german', 'poulenc': 'french', \n", | |
" 'bach': 'german', 'scarlatti': 'italian', 'vivaldi': 'italian', 'balakirev': 'russian', \n", | |
" 'rorem': 'american', 'albert': 'american', 'castello': 'italian', 'telemann': 'german', \n", | |
" 'clarke.r': 'english', 'puccini': 'italian', 'delibes': 'french', 'jolivet': 'french', \n", | |
" 'strauss': 'german', 'rachmaninov': 'russian', 'chesnokov': 'russian', 'shikele': 'american', \n", | |
" 'williams':'american'\n", | |
" \n", | |
" }\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"composer_period = {'hindemith': 'modern', 'debussy': 'modern', 'mozart': 'classical', 'liszt': 'romantic',\n", | |
" 'schumann': 'romantic', 'faure':'romantic', 'ravel': 'modern', 'saint-saens': 'romantic', \n", | |
" 'saint-saans': 'romantic', 'rheinberger': 'romantic', 'korngold': 'modern',\n", | |
" 'chopin': 'romantic', 'tchaikovsky': 'romantic', 'brahms': 'romantic', 'boccherini': 'classical', \n", | |
" 'messiaen': 'modern', 'barber': 'modern', 'rachmaninoff': 'romantic', 'schubert': 'romantic', \n", | |
" 'schoenberg': 'modern', 'silvestrov': 'modern', 'sylvestrov': 'modern', 'mendelssohn': 'romantic', \n", | |
" 'beethoven': 'classical', 'prokofiev': 'modern', 'shostakovich': 'modern', 'khachaturian': 'modern', \n", | |
" 'britten': 'modern', 'mahler': 'romantic', 'wagner': 'romantic', 'berlioz': 'romantic', \n", | |
" 'roussel': 'modern', 'haydn': 'classical', 'bruckner': 'romantic', 'franck': 'romantic', \n", | |
" 'sibelius': 'romantic', 'rossini': 'classical', 'copland': 'modern', 'berio': 'modern', \n", | |
" 'dvorak': 'romantic', 'stravinsky': 'modern', 'bates': 'modern', 'muhly': 'modern', \n", | |
" 'nesbett': 'renaissance', 'byrd': 'renaissance', 'praetorius': 'renaissance', 'schifrin': 'modern', \n", | |
" 'lutoslawski': 'modern', 'bartók': 'modern', 'bartok': 'modern', 'rzewski': 'modern', \n", | |
" 'martin': 'modern', 'baran': 'modern', 'loeffler': 'modern', 'poulenc': 'modern', \n", | |
" 'penderecki': 'modern', 'bach': 'baroque', 'scarlatti': 'baroque', 'vivaldi': 'baroque', \n", | |
" 'balakirev': 'romantic', 'rorem': 'modern', 'martinu': 'modern', 'stamitz': 'baroque', \n", | |
" 'albert': 'modern', 'ginastera': 'modern', 'castello': 'baroque', 'telemann': 'baroque', \n", | |
" 'clarke': 'modern', 'puccini': 'romantic', 'delibes': 'romantic', 'jolivet': 'modern', \n", | |
" 'strauss': 'romantic', 'zyman': 'modern', 'rachmaninov': 'romantic', 'chesnokov': 'modern', \n", | |
" 'krstic': 'modern', 'shikele': 'modern', 'williams':'modern'\n", | |
" }" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Extraction and cleaning of data" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Data were downloaded from various websites, cleaned and stored in a pandas data frame where each row corresponds to a single classical music event. Main Python libraries used at this stage are Requests, BeautifulSoup, and pandas. The data frame contains information on the event date, time, city, venue, price, program, performers, and link to the event page. Three extra columns are generated to store 'tags' for each event, that will be later used by the recommendation engine." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_rel_links_KC(genre): # genre is CLA for classical, CHA for chamber\n", | |
" # Use requests.get to download the page.\n", | |
" page = requests.get('http://www.kennedy-center.org/calendar/genre/' + genre) \n", | |
" soup = BeautifulSoup(page.text, \"lxml\")\n", | |
"\n", | |
" # Get all relative links to individual concerts\n", | |
" events = soup.select('h4') \n", | |
" links = [get_link_KC(event) for event in events]\n", | |
" return links" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Extract relative urls for each event\n", | |
"def get_link_KC(event):\n", | |
" rel_url = re.search( '<a\\shref=\"(.+)\">' , str(event)).group(1)\n", | |
" return rel_url" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns a list of tuples (<Performer name>, <role>)\n", | |
"def get_performers_KC(blurb):\n", | |
" if not re.search(r'blurbpadding\">[\\s]*(\\n?(\\w+\\s\\w+),\\s(\\w+)<br/>\\r*)+', str(blurb)):\n", | |
" return None\n", | |
" performers = re.search(r'blurbpadding\">[\\s]*(\\n?(\\w+\\s\\w+),\\s(\\w+)<br/>\\r*)+', str(blurb)).group(0)\n", | |
" performers = list(filter(lambda x: (len(x)>4),performers.split('<br/>')))\n", | |
" performers = list(map(lambda x: re.search('([A-Z].+),\\s(.+)', x),performers))\n", | |
" performers = [[performer.group(1), performer.group(2)] for performer in performers]\n", | |
" return performers" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns a list of lists [<Composer name>, <piece name>]\n", | |
"def get_program_KC(soup):\n", | |
"\n", | |
" composer_re = re.compile(r'<a href=\"/artist/index/[\\w]+\">(.+)</a>')\n", | |
" composition_re = re.compile(r'<a href=\"/artist/composition/[\\w]+\">(.+)</a>')\n", | |
" TAG_RE = re.compile(r'<[^>]+>')\n", | |
" #composers = re.findall(composer_re, str(soup))\n", | |
" #if composers:\n", | |
" # compositions = re.findall(composition_re, str(soup))\n", | |
" # compositions = [TAG_RE.sub('', composition) for composition in compositions]\n", | |
" # pieces = list(zip(composers, compositions))\n", | |
"\n", | |
" #else:\n", | |
" blurb = soup.select('div.blurbpadding') \n", | |
" pieces = re.findall(r'<strong>(.+</strong>[:|,].*)', str(blurb))\n", | |
" TAG_RE = re.compile(r'<[^>]+>')\n", | |
" pieces = [TAG_RE.sub('', piece) for piece in pieces]\n", | |
" pieces = [piece for piece in pieces if len(piece)<60]\n", | |
" pieces = [re.compile(r'\\xa0').sub('', piece) for piece in pieces]\n", | |
" if (pieces) and (':' in pieces[0]):\n", | |
" pieces = [piece.split(':') for piece in pieces]\n", | |
" else:\n", | |
" pieces = [piece.split(',') for piece in pieces]\n", | |
" \n", | |
" if pieces == []:\n", | |
" pieces = ['Program: TBD']\n", | |
" else:\n", | |
" for piece in pieces:\n", | |
" piece[0] = piece[0].title()\n", | |
" \n", | |
" return pieces" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns the range of ticket proces\n", | |
"def get_price_KC(soup):\n", | |
" price = soup.find('div', {'class': re.compile(r'price*')}).text\n", | |
" price = re.search(r'(\\$\\d+\\.\\d\\d(\\s-\\s\\$\\d+\\.\\d\\d)?)', price).group(0) \n", | |
" price = re.compile(r'\\$').sub('', price)\n", | |
" return price" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Washington Conservatory\n", | |
"def get_rel_links_WC(): \n", | |
" # Download the page.\n", | |
" page = requests.get('http://www.washingtonconservatory.org/html/concerts.htm#professional') \n", | |
" soup = BeautifulSoup(page.text, \"lxml\")\n", | |
" \n", | |
" # Get all relative links to individual concerts\n", | |
" links = [link['href'] for link in soup.find_all(\"a\", {'href': re.compile(r'concerts1819_.*')})]\n", | |
" return links" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get program for Washington Conservatory\n", | |
"def get_program_WC(soup):\n", | |
" program = soup.find_all('li')\n", | |
" program = [item for item in program if item.text != '']\n", | |
" YEAR_RE = re.compile(r'\\([\\d-]+\\)')\n", | |
" if not program:\n", | |
" program = ['Program: TBD']\n", | |
" else: \n", | |
" program = [item.text for item in program]\n", | |
" program = [YEAR_RE.sub('', item) for item in program]\n", | |
" program = [item.split(':') for item in program]\n", | |
" for index, item in enumerate(program):\n", | |
" program[index] = [x.strip() for x in program[index]]\n", | |
" \n", | |
" return program" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get event date and time\n", | |
"def get_date_WC(soup):\n", | |
" date = soup.find('p', {'class': 'center', 'style': re.compile(r'position: relative.*')}).text.strip().split('\\n')[0]\n", | |
" date = pd.Timestamp(date)\n", | |
" return date" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Return a list of performers\n", | |
"def get_performers_WC(soup):\n", | |
" performers = soup.find('h3', {'style': re.compile(r'margin-left: 20px.*')})\n", | |
" performers = performers.findChildren()[0]\n", | |
" #performers = [item.strip() for item in performers if ',' in item]\n", | |
" performers = re.findall('([A-Z][A-Z]+\\.?-?\\s?[A-Z]*\\.?-?\\s?[A-Z]*\\.?,?\\s?[a-z]*\\s?[a-z]*\\s?[a-z]*)', performers.text)\n", | |
" performers = [performer.strip() for performer in performers]\n", | |
" performers = [performer.split(', ') for performer in performers]\n", | |
" if not performers:\n", | |
" return ['Performers: TBD']\n", | |
" for performer in performers:\n", | |
" if len(performer) == 1:\n", | |
" performer[0] = performer[0].title()\n", | |
" else:\n", | |
" performer[0] = performer[0].title()\n", | |
" performer[1] = performer[1].lower() \n", | |
" \n", | |
" return performers" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get event links from Friday Morning Music Club, Inc.\n", | |
"def get_links_FMMC():\n", | |
" # Download the page.\n", | |
" links = []\n", | |
" for i in range(4):\n", | |
" page = requests.get('http://www.fmmc.org/events/list/?tribe_paged='+str(i+1)+'&tribe_event_display=list') \n", | |
" soup = BeautifulSoup(page.text, \"lxml\")\n", | |
"\n", | |
" # Get all relative links to individual concerts\n", | |
" links.extend([link['href'] for link in soup.find_all(\"a\", {'class': 'tribe-event-url'})])\n", | |
" links = [link for link in links if ('concert' in link) | ('orchestra' in link) | ('chorale' in link)]\n", | |
" return links " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get event date and time for Friday Morning Music Club, Inc.\n", | |
"def get_date_FMMC(soup):\n", | |
" date = soup.find_all('abbr', {'class': re.compile(r'.*tribe-events-start.*')})\n", | |
" date = date[0]['title']\n", | |
" time = soup.find_all('div', {'class': re.compile(r'.*tribe-events-start.*')})\n", | |
" time = time[0].text.strip().split(' - ')[0]\n", | |
" return pd.Timestamp(date + ' ' + time)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get program for Friday Morning Music Club, Inc.\n", | |
"def get_program_FMMC(soup):\n", | |
" program = soup.find_all('div', {'class': re.compile(r'tribe-events-single-event-description.*')})\n", | |
" pieces = re.findall('[A-Z]+[:|,].*', str(program[0]))\n", | |
" performer_re = re.compile(r'<strong>[\\w|\\s]*</strong>')\n", | |
" tag_re = re.compile(r'</?\\w+/?>')\n", | |
" pieces = [re.sub(performer_re, '', item) for item in pieces]\n", | |
" pieces = [re.sub('\\.?\\s,.*', '', item) for item in pieces]\n", | |
" pieces = [item.split(':') for item in pieces]\n", | |
" pieces = [item for item in pieces if len(item)>1]\n", | |
" pieces = [[item[0].title().strip(), re.sub(tag_re, '', item[1]).strip()] for item in pieces]\n", | |
" return pieces" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 19, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get price for Friday Morning Music Club, Inc.\n", | |
"def get_price_FMMC(soup):\n", | |
" price = soup.find('dd', {'class': 'tribe-meta-value'})\n", | |
" price = price.text.strip()\n", | |
" if re.search(r'[F|f]ree', price):\n", | |
" price = 'Free'\n", | |
" elif re.search(r'\\d?\\d?\\d\\.00', price):\n", | |
" price = re.findall(r'\\d?\\d?\\d\\.00', price)[0]\n", | |
" else:\n", | |
" price = 'N/A'\n", | |
" return price" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 20, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get venue for Friday Morning Music Club, Inc.\n", | |
"def get_venue_FMMC(soup):\n", | |
" venue = soup.find('dd', {'class': 'tribe-venue'})\n", | |
" venue = venue.text.strip()\n", | |
" return venue" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 21, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get relative links from New York Philharmonic\n", | |
"def get_links_NYPHIL():\n", | |
" # Download the page.\n", | |
" page = requests.get('https://nyphil.org/calendar?season=19&page=all') \n", | |
" soup = BeautifulSoup(page.text, \"lxml\")\n", | |
" # Get all relative links to individual concerts\n", | |
" links = soup.find_all(\"a\", {'href': re.compile(r'/concerts-tickets/1819.*') })\n", | |
" return list(set([link['href'] for link in links])) " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 22, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get program for New York Philharmonic\n", | |
"# Program is returned as a list of tuples, may want to change it to list of lists\n", | |
"def get_program_NYPHIL(soup):\n", | |
" #page = requests.get('https://nyphil.org'+link+'#program')\n", | |
" #soup = BeautifulSoup(page.text, \"lxml\")\n", | |
" program = soup.find_all('div', {'id': re.compile(r'body_0_Program.*') })\n", | |
" if not program:\n", | |
" return ['Program: TBA']\n", | |
" composers = list(program)[0].find_all('div', {'class': 'col1'})\n", | |
" composers = [item.text.strip().split(' ')[-1] for item in composers]\n", | |
" if composers[0] == '':\n", | |
" return ['Program: TBA']\n", | |
" pieces = list(program)[0].find_all('div', {'class': 'col2'})\n", | |
" pieces = [item.text.strip() for item in pieces]\n", | |
" return list(zip(composers, pieces))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 23, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_performers_NYPHIL(soup):\n", | |
" #page = requests.get('https://nyphil.org'+link+'#program')\n", | |
" #soup = BeautifulSoup(page.text, \"lxml\")\n", | |
" program = soup.find_all('div', {'id': re.compile(r'body_0_ArtistList1.*') })\n", | |
" performers = list(program)[0].find_all('div', {'class': 'col1'})\n", | |
" performers = [item.text.strip() for item in performers]\n", | |
" roles = list(program)[0].find_all('div', {'class': 'col2'})\n", | |
" roles = [item.text.strip().lower() for item in roles]\n", | |
" return list(zip(performers, roles))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 24, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_date_NYPHIL(soup):\n", | |
" #page = requests.get('https://nyphil.org'+link)\n", | |
" #soup = BeautifulSoup(page.text, \"lxml\")\n", | |
" days = [item.text for item in soup.findAll('p', {'class': 'date teal'})]\n", | |
" months = [item.text for item in soup.findAll('p', {'class': re.compile(r'month')})]\n", | |
" times = re.findall(r'.*day, (\\d?\\d:\\d\\d [A|P]M)', soup.text)\n", | |
" dates = []\n", | |
" for i in range(len(times)):\n", | |
" dates.append(pd.Timestamp(str(days[i] + ' ' + months[i] + ' ' + times[i])))\n", | |
" return dates" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 25, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_price_NYPHIL(soup):\n", | |
" #page = requests.get('https://nyphil.org'+link)\n", | |
" #soup = BeautifulSoup(page.text, \"lxml\")\n", | |
" price = soup.find('div', {'id': 'body_0_priceRangeRegion'})\n", | |
" if not price:\n", | |
" return 'Price: N/A'\n", | |
" else:\n", | |
" price = price.text\n", | |
" if re.findall('(\\d?\\d\\d)-(\\d?\\d\\d)', price):\n", | |
" price = re.findall('(\\d?\\d\\d)-(\\d?\\d\\d)', price)\n", | |
" price = price[0][0] + '.00' + ' - ' + price[0][1] + '.00'\n", | |
" elif re.findall('(\\d?\\d\\d)', price):\n", | |
" price = re.findall('(\\d?\\d\\d)', price)\n", | |
" price = price[0] + '.00'\n", | |
" elif re.findall('Free', price):\n", | |
" price = 'Free'\n", | |
" return price" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 26, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_venue_NYPHIL(soup):\n", | |
" venue = soup.find('div', {'class': 'col33'})\n", | |
" venue = venue.find('h2').text\n", | |
" return venue" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
" " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 66, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class Event():\n", | |
" def __init__(self, program=[], performers=[], date=None, price=None, \n", | |
" place=None, city=None, tags=[], tags_simple=[], link=None):\n", | |
" self.program = program\n", | |
" self.performers = performers\n", | |
" self.date = date\n", | |
" self.price = price\n", | |
" self.place = place\n", | |
" self.city = city\n", | |
" self.tags = tags\n", | |
" self.tags_simple = tags_simple\n", | |
" self.link = link\n", | |
" \n", | |
" def addTag(self, tag):\n", | |
" self.tags.append(tag)\n", | |
" \n", | |
" def printEvent(self):\n", | |
" date_str = self.date.strftime('%A, %B {}, %Y {}:%M %p'.format(self.date.day, self.date.hour%12))\n", | |
" print(date_str)\n", | |
" print(self.performers)\n", | |
" print(self.program)\n", | |
" print(self.place)\n", | |
" print(self.price)\n", | |
" print('\\n')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 67, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns a list of event objects from Washington Conservatory\n", | |
"def getEvents_NYPHIL():\n", | |
" NYPHIL_URL = 'https://nyphil.org'\n", | |
" events = []\n", | |
" for link in get_links_NYPHIL():\n", | |
" link = NYPHIL_URL + link\n", | |
" concertPage = requests.get(link) \n", | |
" soup = BeautifulSoup(concertPage.text, \"lxml\")\n", | |
" program = get_program_NYPHIL(soup)\n", | |
" dates = get_date_NYPHIL(soup)\n", | |
" performers = get_performers_NYPHIL(soup)\n", | |
" price = get_price_NYPHIL(soup)\n", | |
" place = get_venue_NYPHIL(soup)\n", | |
" for date in dates:\n", | |
" events.append(Event(program=program, performers=performers, date=date, \n", | |
" place=place, city='NYC', price=price, link=link))\n", | |
" return events" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 68, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns a list of event objects from Washington Conservatory\n", | |
"def getEvents_WC():\n", | |
" WASHINGTON_CONSERVATORY_URL = 'http://www.washingtonconservatory.org/html/'\n", | |
" events = []\n", | |
" for link in get_rel_links_WC():\n", | |
" link = WASHINGTON_CONSERVATORY_URL + link\n", | |
" concertPage = requests.get(link) \n", | |
" soup = BeautifulSoup(concertPage.text, \"lxml\")\n", | |
" program = get_program_WC(soup)\n", | |
" date = get_date_WC(soup)\n", | |
" performers = get_performers_WC(soup)\n", | |
" if not pd.isnull(date):\n", | |
" events.append(Event(program=program, performers=performers, date=date, \n", | |
" place='Washington Conservatory', city='WDC', price='Donation', link=link))\n", | |
" return events" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 69, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns a list of event objects from Kennedy Center\n", | |
"def getEvents_KC(genre):\n", | |
" KENNEDY_URL = 'http://www.kennedy-center.org'\n", | |
" events = []\n", | |
" for link in get_rel_links_KC(genre):\n", | |
" link = KENNEDY_URL + link\n", | |
" concertPage = requests.get(link) \n", | |
" soup = BeautifulSoup(concertPage.text, \"lxml\")\n", | |
" date = pd.Timestamp(soup.find('meta', {'name': 'StartDate'})['content']).to_pydatetime()\n", | |
" #price = soup.find('div', {'class': re.compile(r'price*')}).text\n", | |
" #price = re.search(r'(\\$\\d+\\.\\d\\d(\\s-\\s\\$\\d+\\.\\d\\d)?)', price).group(0) \n", | |
" price = get_price_KC(soup)\n", | |
" blurb = soup.select('div.blurbpadding') \n", | |
" if not get_performers_KC(blurb):\n", | |
" continue\n", | |
" performers = get_performers_KC(blurb)\n", | |
" program = get_program_KC(soup)\n", | |
" events.append(Event(program=program, performers=performers, date=date, \n", | |
" place='Kennedy Center', city='WDC', price=price, link=link))\n", | |
" return events" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 70, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Returns a list of event objects from Friday Morning Music Club, Inc.\n", | |
"def getEvents_FMMC():\n", | |
" events = []\n", | |
" for link in get_links_FMMC():\n", | |
" concertPage = requests.get(link) \n", | |
" soup = BeautifulSoup(concertPage.text, \"lxml\")\n", | |
" program = get_program_FMMC(soup)\n", | |
" if not program:\n", | |
" continue\n", | |
" price = get_price_FMMC(soup)\n", | |
" date = get_date_FMMC(soup)\n", | |
" venue = get_venue_FMMC(soup)\n", | |
" \n", | |
" events.append(Event(program=program, date=date, place=venue, \n", | |
" city='WDC', price=price, link=link))\n", | |
" return events" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 112, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def make_price_tag(string):\n", | |
" if re.compile(r'[D|d]onation').search(string):\n", | |
" return(5)\n", | |
" elif re.compile(r'[F|f]ree').search(string):\n", | |
" return(0)\n", | |
" elif string == 'Price: N/A':\n", | |
" return(50)\n", | |
" else:\n", | |
" prices = list(map(float, string.split(' - ')))\n", | |
" if len(prices)==1:\n", | |
" return(prices[0])\n", | |
" else:\n", | |
" return(sum(prices)/2)\n", | |
" " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 71, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def dfFromEvents(event_list):\n", | |
" columns = ['Date', 'Time', 'Venue', 'City', 'Price', 'Performers', 'Program', 'Tags', 'Tags_Simple', 'Link']\n", | |
" df = pd.DataFrame(columns=columns)\n", | |
" for event in event_list:\n", | |
" tags = [] \n", | |
" tags_simple = []\n", | |
" composers = []\n", | |
" tags.extend([performer[1] for performer in event.performers if len(performer)>1])\n", | |
" \n", | |
" \n", | |
" tags = [ re.compile(r'conductor').sub('orchestra', tag) for tag in tags ]\n", | |
" tags_simple = [ re.compile(r'conductor').sub('orchestra', tag) for tag in tags_simple ]\n", | |
" \n", | |
" if 'orchestra' not in tags:\n", | |
" tags.append('chamber')\n", | |
" tags_simple.append('chamber')\n", | |
" else: \n", | |
" tags_simple.append('orchestra')\n", | |
" \n", | |
" tags = [ re.compile(r'\\s?solo\\s?').sub('', tag) for tag in tags ]\n", | |
" tags = [ re.compile(r' and').sub(',', tag) for tag in tags ]\n", | |
" \n", | |
" \n", | |
" composers.extend( [p[0] for p in event.program if len(p[0])>1] )\n", | |
" \n", | |
" \n", | |
" for index in range(len(composers)):\n", | |
" composers[index] = composers[index].lower()\n", | |
" if len(composers[index].split(' ')) > 1:\n", | |
" composers[index] = composers[index].split(' ')[-1]\n", | |
" \n", | |
" \n", | |
" composers = [tag for tag in composers if (not tag.isdigit())] \n", | |
" composers = [unicodedata.normalize('NFKD', tag).encode('ascii','ignore').decode(\"ascii\") for tag in composers]\n", | |
" \n", | |
" composers = [composer for composer in composers if (len(composer)>1) & (composer != 'tbd')]\n", | |
" \n", | |
" tags.extend(set(composers))\n", | |
" \n", | |
" tags_simple.extend(list(set([composer_period[composer] for composer in composers if composer in composer_period.keys()])))\n", | |
" tags_simple.extend(list(set([composer_nationality[composer] for composer in composers if composer in composer_nationality.keys()])))\n", | |
" \n", | |
" tags = [tag for tag in tags if (len(tag)>1) & (tag != 'tbd')]\n", | |
" \n", | |
" \n", | |
" d = {'Date': event.date.date(), 'Time': event.date.time(), 'Venue': event.place, 'City': event.city,\n", | |
" 'Price': event.price, 'Performers': event.performers, 'Program': event.program, \n", | |
" 'Tags': tags, 'Tags_Simple': tags_simple, 'Link': event.link}\n", | |
" df = df.append(pd.Series(d), ignore_index=True)\n", | |
" df['Price_tag'] = df['Price'].apply(make_price_tag)\n", | |
" return df" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 72, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"columns = ['Date', 'Time', 'Venue', 'City', 'Price', 'Performers', \n", | |
" 'Program', 'Tags', 'Tags_Simple', 'Price_tag', 'Link']\n", | |
"df_all = pd.DataFrame(columns = columns)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 74, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Add KC to df_all\n", | |
"df_all = pd.concat([df_all, dfFromEvents(getEvents_KC('CLA')+getEvents_KC('CHA'))]).reset_index().drop(columns='index')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 75, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Add WC to df_all\n", | |
"df_all = pd.concat([df_all, dfFromEvents(getEvents_WC())]).reset_index().drop(columns='index')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 76, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Add FMMC to df_all\n", | |
"df_all = pd.concat([df_all, dfFromEvents(getEvents_FMMC())]).reset_index().drop(columns='index')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 77, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Add NYPHIL to df_all\n", | |
"df_all = pd.concat([df_all, dfFromEvents(getEvents_NYPHIL())]).reset_index().drop(columns='index')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 234, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<style scoped>\n", | |
" .dataframe tbody tr th:only-of-type {\n", | |
" vertical-align: middle;\n", | |
" }\n", | |
"\n", | |
" .dataframe tbody tr th {\n", | |
" vertical-align: top;\n", | |
" }\n", | |
"\n", | |
" .dataframe thead th {\n", | |
" text-align: right;\n", | |
" }\n", | |
"</style>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>City</th>\n", | |
" <th>Date</th>\n", | |
" <th>Link</th>\n", | |
" <th>Performers</th>\n", | |
" <th>Price</th>\n", | |
" <th>Price_tag</th>\n", | |
" <th>Program</th>\n", | |
" <th>Tags</th>\n", | |
" <th>Tags_Simple</th>\n", | |
" <th>Time</th>\n", | |
" <th>Venue</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>0</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-11-01</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[James Gaffigan, conductor], [Simon Trpceski,...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Prokofiev, Symphony No. 3], [Shostakovich, P...</td>\n", | |
" <td>[orchestra, piano, khachaturian, prokofiev, sh...</td>\n", | |
" <td>[orchestra, modern, russian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>1</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-11-29</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor], [Karina Flore...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Britten, War Requiem]]</td>\n", | |
" <td>[orchestra, soprano, tenor, baritone, britten]</td>\n", | |
" <td>[orchestra, modern, english]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-12-06</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor]]</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Mahler, Symphony No. 1, “Titan”]]</td>\n", | |
" <td>[orchestra, mahler]</td>\n", | |
" <td>[orchestra, romantic, austrian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-12-20</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Nicholas McGegan, conductor]]</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[Program: TBD]</td>\n", | |
" <td>[orchestra]</td>\n", | |
" <td>[orchestra]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-01-18</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor], [Renée Flemin...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Schubert, Rosamunde—Overture and incidental...</td>\n", | |
" <td>[orchestra, soprano, schubert/berio, schubert]</td>\n", | |
" <td>[orchestra, romantic, austrian]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>5</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-01-31</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor], [Daniil Trifo...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Beethoven, Piano Concerto No. 5 “Emperor”],...</td>\n", | |
" <td>[orchestra, piano, beethoven, shostakovich]</td>\n", | |
" <td>[orchestra, modern, classical, german, russian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>6</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-02-07</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor], [Nicholas Ang...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Brahms, Tragic Overture], [Schumann, Piano...</td>\n", | |
" <td>[orchestra, piano, schumann, brahms, liszt]</td>\n", | |
" <td>[orchestra, romantic, german, hungarian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>7</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-02-14</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor]]</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Wagner, “Prelude and Liebestod” from Trista...</td>\n", | |
" <td>[orchestra, tchaikovsky, berlioz, wagner]</td>\n", | |
" <td>[orchestra, romantic, german, french, russian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>8</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-02-28</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Lionel Bringuier, conductor], [Gil Shaham, v...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Roussel, The Spider's Feast—symphonic fragm...</td>\n", | |
" <td>[orchestra, violin, stravinsky, roussel, mozart]</td>\n", | |
" <td>[orchestra, modern, classical, austrian, frenc...</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>9</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-03-07</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Christoph Eshenbach, conductor], [Kian Solta...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Haydn, Cello Concerto in D], [Bruckner, Sy...</td>\n", | |
" <td>[orchestra, cello, bruckner, haydn]</td>\n", | |
" <td>[orchestra, romantic, classical, austrian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" City Date Link \\\n", | |
"0 WDC 2018-11-01 http://www.kennedy-center.org/calendar/event/N... \n", | |
"1 WDC 2018-11-29 http://www.kennedy-center.org/calendar/event/N... \n", | |
"2 WDC 2018-12-06 http://www.kennedy-center.org/calendar/event/N... \n", | |
"3 WDC 2018-12-20 http://www.kennedy-center.org/calendar/event/N... \n", | |
"4 WDC 2019-01-18 http://www.kennedy-center.org/calendar/event/N... \n", | |
"5 WDC 2019-01-31 http://www.kennedy-center.org/calendar/event/N... \n", | |
"6 WDC 2019-02-07 http://www.kennedy-center.org/calendar/event/N... \n", | |
"7 WDC 2019-02-14 http://www.kennedy-center.org/calendar/event/N... \n", | |
"8 WDC 2019-02-28 http://www.kennedy-center.org/calendar/event/N... \n", | |
"9 WDC 2019-03-07 http://www.kennedy-center.org/calendar/event/N... \n", | |
"\n", | |
" Performers Price \\\n", | |
"0 [[James Gaffigan, conductor], [Simon Trpceski,... 15.00 - 89.00 \n", | |
"1 [[Gianandrea Noseda, conductor], [Karina Flore... 15.00 - 89.00 \n", | |
"2 [[Gianandrea Noseda, conductor]] 15.00 - 89.00 \n", | |
"3 [[Nicholas McGegan, conductor]] 15.00 - 89.00 \n", | |
"4 [[Gianandrea Noseda, conductor], [Renée Flemin... 15.00 - 89.00 \n", | |
"5 [[Gianandrea Noseda, conductor], [Daniil Trifo... 15.00 - 89.00 \n", | |
"6 [[Gianandrea Noseda, conductor], [Nicholas Ang... 15.00 - 89.00 \n", | |
"7 [[Gianandrea Noseda, conductor]] 15.00 - 89.00 \n", | |
"8 [[Lionel Bringuier, conductor], [Gil Shaham, v... 15.00 - 89.00 \n", | |
"9 [[Christoph Eshenbach, conductor], [Kian Solta... 15.00 - 89.00 \n", | |
"\n", | |
" Price_tag Program \\\n", | |
"0 52.0 [[Prokofiev, Symphony No. 3], [Shostakovich, P... \n", | |
"1 52.0 [[Britten, War Requiem]] \n", | |
"2 52.0 [[Mahler, Symphony No. 1, “Titan”]] \n", | |
"3 52.0 [Program: TBD] \n", | |
"4 52.0 [[Schubert, Rosamunde—Overture and incidental... \n", | |
"5 52.0 [[Beethoven, Piano Concerto No. 5 “Emperor”],... \n", | |
"6 52.0 [[Brahms, Tragic Overture], [Schumann, Piano... \n", | |
"7 52.0 [[Wagner, “Prelude and Liebestod” from Trista... \n", | |
"8 52.0 [[Roussel, The Spider's Feast—symphonic fragm... \n", | |
"9 52.0 [[Haydn, Cello Concerto in D], [Bruckner, Sy... \n", | |
"\n", | |
" Tags \\\n", | |
"0 [orchestra, piano, khachaturian, prokofiev, sh... \n", | |
"1 [orchestra, soprano, tenor, baritone, britten] \n", | |
"2 [orchestra, mahler] \n", | |
"3 [orchestra] \n", | |
"4 [orchestra, soprano, schubert/berio, schubert] \n", | |
"5 [orchestra, piano, beethoven, shostakovich] \n", | |
"6 [orchestra, piano, schumann, brahms, liszt] \n", | |
"7 [orchestra, tchaikovsky, berlioz, wagner] \n", | |
"8 [orchestra, violin, stravinsky, roussel, mozart] \n", | |
"9 [orchestra, cello, bruckner, haydn] \n", | |
"\n", | |
" Tags_Simple Time Venue \n", | |
"0 [orchestra, modern, russian] 19:00:00 Kennedy Center \n", | |
"1 [orchestra, modern, english] 19:00:00 Kennedy Center \n", | |
"2 [orchestra, romantic, austrian] 19:00:00 Kennedy Center \n", | |
"3 [orchestra] 19:00:00 Kennedy Center \n", | |
"4 [orchestra, romantic, austrian] 20:00:00 Kennedy Center \n", | |
"5 [orchestra, modern, classical, german, russian] 19:00:00 Kennedy Center \n", | |
"6 [orchestra, romantic, german, hungarian] 19:00:00 Kennedy Center \n", | |
"7 [orchestra, romantic, german, french, russian] 19:00:00 Kennedy Center \n", | |
"8 [orchestra, modern, classical, austrian, frenc... 19:00:00 Kennedy Center \n", | |
"9 [orchestra, romantic, classical, austrian] 19:00:00 Kennedy Center " | |
] | |
}, | |
"execution_count": 234, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"df_all.head(10)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df_all.iloc[:].sort_values(by=['Date'])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"205" | |
] | |
}, | |
"execution_count": 9, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"len(df_all)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df_all[(df_all['Date'] >= datetime.today().date()) & (df_all['City'] == 'NYC')]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 391, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"#dill.dump(df_all, open('df_all_nov3.pkd', 'wb'))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"#df_all = dill.load(open('df_all_nov3.pkd', 'rb'))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Keyword search" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This code generates a list of events in the selected city, whose tags contain the entered keyword, and happening on or after the present day." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 216, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"keyword = 'Schumann'\n", | |
"city = 'WDC'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 222, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<style scoped>\n", | |
" .dataframe tbody tr th:only-of-type {\n", | |
" vertical-align: middle;\n", | |
" }\n", | |
"\n", | |
" .dataframe tbody tr th {\n", | |
" vertical-align: top;\n", | |
" }\n", | |
"\n", | |
" .dataframe thead th {\n", | |
" text-align: right;\n", | |
" }\n", | |
"</style>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>City</th>\n", | |
" <th>Date</th>\n", | |
" <th>Link</th>\n", | |
" <th>Performers</th>\n", | |
" <th>Price</th>\n", | |
" <th>Price_tag</th>\n", | |
" <th>Program</th>\n", | |
" <th>Tags</th>\n", | |
" <th>Tags_Simple</th>\n", | |
" <th>Time</th>\n", | |
" <th>Venue</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>6</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-02-07</td>\n", | |
" <td>http://www.kennedy-center.org/calendar/event/N...</td>\n", | |
" <td>[[Gianandrea Noseda, conductor], [Nicholas Ang...</td>\n", | |
" <td>15.00 - 89.00</td>\n", | |
" <td>52.0</td>\n", | |
" <td>[[Brahms, Tragic Overture], [Schumann, Piano...</td>\n", | |
" <td>[orchestra, piano, schumann, brahms, liszt]</td>\n", | |
" <td>[orchestra, romantic, german, hungarian]</td>\n", | |
" <td>19:00:00</td>\n", | |
" <td>Kennedy Center</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" City Date Link \\\n", | |
"6 WDC 2019-02-07 http://www.kennedy-center.org/calendar/event/N... \n", | |
"\n", | |
" Performers Price \\\n", | |
"6 [[Gianandrea Noseda, conductor], [Nicholas Ang... 15.00 - 89.00 \n", | |
"\n", | |
" Price_tag Program \\\n", | |
"6 52.0 [[Brahms, Tragic Overture], [Schumann, Piano... \n", | |
"\n", | |
" Tags \\\n", | |
"6 [orchestra, piano, schumann, brahms, liszt] \n", | |
"\n", | |
" Tags_Simple Time Venue \n", | |
"6 [orchestra, romantic, german, hungarian] 19:00:00 Kennedy Center " | |
] | |
}, | |
"execution_count": 222, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"df_all[df_all.apply(lambda x: (keyword.lower() in x['Tags']) \n", | |
" or (keyword.lower() in x['Tags_Simple'])\n", | |
" , axis=1) \n", | |
" & (df_all['City']==city)\n", | |
" & (df_all['Date'] >= datetime.today().date())\n", | |
" ]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
" " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Recommendation Engine\n", | |
"Here we use a Nearest Neighbors model from scikit-learn to create a recommendation engine. There are two different recommendation methods used. The first method finds multiple recommendations for a single input event. The second method finds one (or more) recommendation(s) based on a group of input events. The second method takes a weighted average of the input events before finding a recommendation. \n", | |
"\n", | |
"Both methods use a combination of one-hot-encoded categorical features (composer nationalities, period that each piece belongs to, and whether the piece is orchestral or chamber music) along with the price value, which is a continuous feature.\n", | |
"\n", | |
"The results from the recommendation are presented below." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class DictEncoder(base.BaseEstimator, base.TransformerMixin):\n", | |
" \n", | |
" def __init__(self, col):\n", | |
" self.col = col\n", | |
" \n", | |
" def fit(self, X, y=None):\n", | |
" return self\n", | |
" \n", | |
" def transform(self, X):\n", | |
" \n", | |
" def to_dict(l):\n", | |
" try:\n", | |
" return {x: 1 for x in l}\n", | |
" except TypeError:\n", | |
" return {}\n", | |
" \n", | |
" return X[self.col].apply(to_dict)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 116, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"tag_pipe = Pipeline([('encoder', DictEncoder('Tags_Simple')),\n", | |
" ('vectorizer', DictVectorizer())])\n", | |
"features = tag_pipe.fit_transform(df_all)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This is a helper function that combines the categorical features with the price column that will be used in the recommendation engine:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 117, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def add_feature(X, feature_to_add):\n", | |
" \"\"\"\n", | |
" Returns sparse feature matrix with added feature.\n", | |
" feature_to_add can also be a list of features.\n", | |
" \"\"\"\n", | |
" from scipy.sparse import csr_matrix, hstack\n", | |
" return hstack([X, csr_matrix(feature_to_add).T], 'csr')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 118, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Add normalized ticket prices to the feature matrix\n", | |
"features = add_feature(features, df_all['Price_tag'] / np.max(df_all['Price_tag']))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 119, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"nn = NearestNeighbors(n_neighbors=4).fit(features)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 120, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<style scoped>\n", | |
" .dataframe tbody tr th:only-of-type {\n", | |
" vertical-align: middle;\n", | |
" }\n", | |
"\n", | |
" .dataframe tbody tr th {\n", | |
" vertical-align: top;\n", | |
" }\n", | |
"\n", | |
" .dataframe thead th {\n", | |
" text-align: right;\n", | |
" }\n", | |
"</style>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>City</th>\n", | |
" <th>Date</th>\n", | |
" <th>Link</th>\n", | |
" <th>Performers</th>\n", | |
" <th>Price</th>\n", | |
" <th>Price_tag</th>\n", | |
" <th>Program</th>\n", | |
" <th>Tags</th>\n", | |
" <th>Tags_Simple</th>\n", | |
" <th>Time</th>\n", | |
" <th>Venue</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>34</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-10-06</td>\n", | |
" <td>http://www.washingtonconservatory.org/html/con...</td>\n", | |
" <td>[[Pressenda Chamber Players]]</td>\n", | |
" <td>Donation</td>\n", | |
" <td>5.0</td>\n", | |
" <td>[[Maurice Ravel, Trio in A Minor], [Peter Tcha...</td>\n", | |
" <td>[chamber, tchaikovsky, ravel]</td>\n", | |
" <td>[chamber, romantic, modern, french, russian]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Washington Conservatory</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" City Date Link \\\n", | |
"34 WDC 2018-10-06 http://www.washingtonconservatory.org/html/con... \n", | |
"\n", | |
" Performers Price Price_tag \\\n", | |
"34 [[Pressenda Chamber Players]] Donation 5.0 \n", | |
"\n", | |
" Program \\\n", | |
"34 [[Maurice Ravel, Trio in A Minor], [Peter Tcha... \n", | |
"\n", | |
" Tags \\\n", | |
"34 [chamber, tchaikovsky, ravel] \n", | |
"\n", | |
" Tags_Simple Time \\\n", | |
"34 [chamber, romantic, modern, french, russian] 20:00:00 \n", | |
"\n", | |
" Venue \n", | |
"34 Washington Conservatory " | |
] | |
}, | |
"execution_count": 120, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"df_all.iloc[[34]]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Recommending events that are similar to a selected single event\n", | |
"Below is a list of events recommended based on the input event in the cell above:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 121, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<style scoped>\n", | |
" .dataframe tbody tr th:only-of-type {\n", | |
" vertical-align: middle;\n", | |
" }\n", | |
"\n", | |
" .dataframe tbody tr th {\n", | |
" vertical-align: top;\n", | |
" }\n", | |
"\n", | |
" .dataframe thead th {\n", | |
" text-align: right;\n", | |
" }\n", | |
"</style>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>City</th>\n", | |
" <th>Date</th>\n", | |
" <th>Link</th>\n", | |
" <th>Performers</th>\n", | |
" <th>Price</th>\n", | |
" <th>Price_tag</th>\n", | |
" <th>Program</th>\n", | |
" <th>Tags</th>\n", | |
" <th>Tags_Simple</th>\n", | |
" <th>Time</th>\n", | |
" <th>Venue</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>34</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-10-06</td>\n", | |
" <td>http://www.washingtonconservatory.org/html/con...</td>\n", | |
" <td>[[Pressenda Chamber Players]]</td>\n", | |
" <td>Donation</td>\n", | |
" <td>5.0</td>\n", | |
" <td>[[Maurice Ravel, Trio in A Minor], [Peter Tcha...</td>\n", | |
" <td>[chamber, tchaikovsky, ravel]</td>\n", | |
" <td>[chamber, romantic, modern, french, russian]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Washington Conservatory</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>38</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-02-02</td>\n", | |
" <td>http://www.washingtonconservatory.org/html/con...</td>\n", | |
" <td>[[Pressenda Chamber Players]]</td>\n", | |
" <td>Donation</td>\n", | |
" <td>5.0</td>\n", | |
" <td>[[Claude Debussy, String Quartet in G Minor, O...</td>\n", | |
" <td>[chamber, debussy, faure]</td>\n", | |
" <td>[chamber, romantic, modern, french]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Washington Conservatory</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>35</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-11-03</td>\n", | |
" <td>http://www.washingtonconservatory.org/html/con...</td>\n", | |
" <td>[[Sofya Gulyak, solo piano]]</td>\n", | |
" <td>Donation</td>\n", | |
" <td>5.0</td>\n", | |
" <td>[[Lyadov, 3 Pieces, Op. 11], [Tchaikovsky-Plet...</td>\n", | |
" <td>[piano, chamber, rachmaninoff, prokofiev, lyad...</td>\n", | |
" <td>[chamber, romantic, modern, russian]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Washington Conservatory</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>49</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-11-11</td>\n", | |
" <td>http://www.fmmc.org/event/lyceum-concert-serie...</td>\n", | |
" <td>[]</td>\n", | |
" <td>Free</td>\n", | |
" <td>0.0</td>\n", | |
" <td>[[Poulenc, Flute Sonata. IBERT], [Brahms, Six ...</td>\n", | |
" <td>[chamber, stravinsky, brahms, poulenc]</td>\n", | |
" <td>[chamber, romantic, modern, german, french, ru...</td>\n", | |
" <td>15:00:00</td>\n", | |
" <td>The Lyceum: Alexandria’s History Museum</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" City Date Link \\\n", | |
"34 WDC 2018-10-06 http://www.washingtonconservatory.org/html/con... \n", | |
"38 WDC 2019-02-02 http://www.washingtonconservatory.org/html/con... \n", | |
"35 WDC 2018-11-03 http://www.washingtonconservatory.org/html/con... \n", | |
"49 WDC 2018-11-11 http://www.fmmc.org/event/lyceum-concert-serie... \n", | |
"\n", | |
" Performers Price Price_tag \\\n", | |
"34 [[Pressenda Chamber Players]] Donation 5.0 \n", | |
"38 [[Pressenda Chamber Players]] Donation 5.0 \n", | |
"35 [[Sofya Gulyak, solo piano]] Donation 5.0 \n", | |
"49 [] Free 0.0 \n", | |
"\n", | |
" Program \\\n", | |
"34 [[Maurice Ravel, Trio in A Minor], [Peter Tcha... \n", | |
"38 [[Claude Debussy, String Quartet in G Minor, O... \n", | |
"35 [[Lyadov, 3 Pieces, Op. 11], [Tchaikovsky-Plet... \n", | |
"49 [[Poulenc, Flute Sonata. IBERT], [Brahms, Six ... \n", | |
"\n", | |
" Tags \\\n", | |
"34 [chamber, tchaikovsky, ravel] \n", | |
"38 [chamber, debussy, faure] \n", | |
"35 [piano, chamber, rachmaninoff, prokofiev, lyad... \n", | |
"49 [chamber, stravinsky, brahms, poulenc] \n", | |
"\n", | |
" Tags_Simple Time \\\n", | |
"34 [chamber, romantic, modern, french, russian] 20:00:00 \n", | |
"38 [chamber, romantic, modern, french] 20:00:00 \n", | |
"35 [chamber, romantic, modern, russian] 20:00:00 \n", | |
"49 [chamber, romantic, modern, german, french, ru... 15:00:00 \n", | |
"\n", | |
" Venue \n", | |
"34 Washington Conservatory \n", | |
"38 Washington Conservatory \n", | |
"35 Washington Conservatory \n", | |
"49 The Lyceum: Alexandria’s History Museum " | |
] | |
}, | |
"execution_count": 121, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"dists, indices = nn.kneighbors(features[34])\n", | |
"df_all.iloc[indices[0]]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Taking a weighted average of a group of selected events to recommend a single event" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Below is a group of events based on which a recommendation will be generated:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 122, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<style scoped>\n", | |
" .dataframe tbody tr th:only-of-type {\n", | |
" vertical-align: middle;\n", | |
" }\n", | |
"\n", | |
" .dataframe tbody tr th {\n", | |
" vertical-align: top;\n", | |
" }\n", | |
"\n", | |
" .dataframe thead th {\n", | |
" text-align: right;\n", | |
" }\n", | |
"</style>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>City</th>\n", | |
" <th>Date</th>\n", | |
" <th>Link</th>\n", | |
" <th>Performers</th>\n", | |
" <th>Price</th>\n", | |
" <th>Price_tag</th>\n", | |
" <th>Program</th>\n", | |
" <th>Tags</th>\n", | |
" <th>Tags_Simple</th>\n", | |
" <th>Time</th>\n", | |
" <th>Venue</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>34</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-10-06</td>\n", | |
" <td>http://www.washingtonconservatory.org/html/con...</td>\n", | |
" <td>[[Pressenda Chamber Players]]</td>\n", | |
" <td>Donation</td>\n", | |
" <td>5.0</td>\n", | |
" <td>[[Maurice Ravel, Trio in A Minor], [Peter Tcha...</td>\n", | |
" <td>[chamber, tchaikovsky, ravel]</td>\n", | |
" <td>[chamber, romantic, modern, french, russian]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Washington Conservatory</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>47</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-11-10</td>\n", | |
" <td>http://www.fmmc.org/event/greenspring-concert-...</td>\n", | |
" <td>[]</td>\n", | |
" <td>Free</td>\n", | |
" <td>0.0</td>\n", | |
" <td>[[Rheinberger, Nonet], [Takemitsu, In the Wood...</td>\n", | |
" <td>[chamber, takemitsu, rheinberger, balakirev]</td>\n", | |
" <td>[chamber, romantic, german, russian]</td>\n", | |
" <td>15:00:00</td>\n", | |
" <td>Greenspring Retirement Community</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>51</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2018-11-13</td>\n", | |
" <td>http://www.fmmc.org/event/womans-club-concert-...</td>\n", | |
" <td>[]</td>\n", | |
" <td>Free</td>\n", | |
" <td>0.0</td>\n", | |
" <td>[[Tchaikovsky, Selected Romances], [Haydn, Var...</td>\n", | |
" <td>[chamber, schikele, tchaikovsky, haydn]</td>\n", | |
" <td>[chamber, romantic, classical, austrian, russian]</td>\n", | |
" <td>12:00:00</td>\n", | |
" <td>Woman’s Club of Chevy Chase</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" City Date Link \\\n", | |
"34 WDC 2018-10-06 http://www.washingtonconservatory.org/html/con... \n", | |
"47 WDC 2018-11-10 http://www.fmmc.org/event/greenspring-concert-... \n", | |
"51 WDC 2018-11-13 http://www.fmmc.org/event/womans-club-concert-... \n", | |
"\n", | |
" Performers Price Price_tag \\\n", | |
"34 [[Pressenda Chamber Players]] Donation 5.0 \n", | |
"47 [] Free 0.0 \n", | |
"51 [] Free 0.0 \n", | |
"\n", | |
" Program \\\n", | |
"34 [[Maurice Ravel, Trio in A Minor], [Peter Tcha... \n", | |
"47 [[Rheinberger, Nonet], [Takemitsu, In the Wood... \n", | |
"51 [[Tchaikovsky, Selected Romances], [Haydn, Var... \n", | |
"\n", | |
" Tags \\\n", | |
"34 [chamber, tchaikovsky, ravel] \n", | |
"47 [chamber, takemitsu, rheinberger, balakirev] \n", | |
"51 [chamber, schikele, tchaikovsky, haydn] \n", | |
"\n", | |
" Tags_Simple Time \\\n", | |
"34 [chamber, romantic, modern, french, russian] 20:00:00 \n", | |
"47 [chamber, romantic, german, russian] 15:00:00 \n", | |
"51 [chamber, romantic, classical, austrian, russian] 12:00:00 \n", | |
"\n", | |
" Venue \n", | |
"34 Washington Conservatory \n", | |
"47 Greenspring Retirement Community \n", | |
"51 Woman’s Club of Chevy Chase " | |
] | |
}, | |
"execution_count": 122, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"indices_fav = np.array([34, 47, 51])\n", | |
"df_all.iloc[indices_fav]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 123, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Add ratings (equal ratings for all three events)\n", | |
"ratings = np.ones(len(indices_fav))\n", | |
"# Calculate an 'average' event\n", | |
"weighted_avg_concert = ratings.reshape(1,-1).dot(features[indices_fav,:].toarray()) / len(ratings)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 124, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# List of indices with selected indices removed\n", | |
"indices_rem = np.arange(len(features.toarray()))\n", | |
"indices_rem = np.delete(indices_rem, indices_fav)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 125, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Nearest neighbors model trained on the remaining set of events\n", | |
"nn2 = NearestNeighbors(n_neighbors=1).fit(features[indices_rem,:])\n", | |
"dists, indices = nn2.kneighbors(weighted_avg_concert)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Result from the recommendation engine:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 131, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<style scoped>\n", | |
" .dataframe tbody tr th:only-of-type {\n", | |
" vertical-align: middle;\n", | |
" }\n", | |
"\n", | |
" .dataframe tbody tr th {\n", | |
" vertical-align: top;\n", | |
" }\n", | |
"\n", | |
" .dataframe thead th {\n", | |
" text-align: right;\n", | |
" }\n", | |
"</style>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>City</th>\n", | |
" <th>Date</th>\n", | |
" <th>Link</th>\n", | |
" <th>Performers</th>\n", | |
" <th>Price</th>\n", | |
" <th>Price_tag</th>\n", | |
" <th>Program</th>\n", | |
" <th>Tags</th>\n", | |
" <th>Tags_Simple</th>\n", | |
" <th>Time</th>\n", | |
" <th>Venue</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>37</th>\n", | |
" <td>WDC</td>\n", | |
" <td>2019-01-05</td>\n", | |
" <td>http://www.washingtonconservatory.org/html/con...</td>\n", | |
" <td>[[Alexander Paley, piano]]</td>\n", | |
" <td>Donation</td>\n", | |
" <td>5.0</td>\n", | |
" <td>[[Chopin, Preludes], [Tchaikovsky, Romeo and J...</td>\n", | |
" <td>[piano, chamber, tchaikovsky, chopin]</td>\n", | |
" <td>[chamber, romantic, russian]</td>\n", | |
" <td>20:00:00</td>\n", | |
" <td>Washington Conservatory</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" City Date Link \\\n", | |
"37 WDC 2019-01-05 http://www.washingtonconservatory.org/html/con... \n", | |
"\n", | |
" Performers Price Price_tag \\\n", | |
"37 [[Alexander Paley, piano]] Donation 5.0 \n", | |
"\n", | |
" Program \\\n", | |
"37 [[Chopin, Preludes], [Tchaikovsky, Romeo and J... \n", | |
"\n", | |
" Tags Tags_Simple \\\n", | |
"37 [piano, chamber, tchaikovsky, chopin] [chamber, romantic, russian] \n", | |
"\n", | |
" Time Venue \n", | |
"37 20:00:00 Washington Conservatory " | |
] | |
}, | |
"execution_count": 131, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"df_all.drop(indices_fav).iloc[indices[0]]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
" " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Map" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The map below displays the location of the events corresponding to the particular date and city shown below. The latitude and longitude values for each venue are stored in a dictionary." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 47, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import folium" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 164, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"venue_coordinates = {'David Geffen Hall': (40.7729, -73.9830), 'Woman’s Club of Chevy Chase': (38.9902, -77.0767), \n", | |
" 'Kennedy Center': (38.8958, -77.0557), 'Dumbarton House': (38.9109, -77.0559), \n", | |
" 'Cultural Arts Center': (38.9869, -77.0261), 'Merkin Hall': (40.7752, -73.9830), \n", | |
" \"The Lyceum: Alexandria’s History Museum\": (38.8038, -77.0476), \n", | |
" 'Washington Conservatory': (38.9656, -77.1379), 'Calvary Baptist Church': (38.8995, -77.0228), \n", | |
" 'Old Town Hall in Fairfax City': (38.8461, -77.3051), \n", | |
" 'The Mansion at Strathmore':(39.0314, -77.1044), \n", | |
" 'Greenspring Retirement Community': (38.7670, -77.2034), \n", | |
" 'Lutheran Church of the Reformation': (38.8902, -77.0029),\n", | |
" 'Stanley H. Kaplan Penthouse': (40.7741, -73.9842), \n", | |
" '92Y': (40.7831, -73.9527)}" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 225, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Select a date and a city\n", | |
"date = '2018-11-10'\n", | |
"city = 'NYC'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 226, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><iframe src=\"data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgPHNjcmlwdD5MX1BSRUZFUl9DQU5WQVMgPSBmYWxzZTsgTF9OT19UT1VDSCA9IGZhbHNlOyBMX0RJU0FCTEVfM0QgPSBmYWxzZTs8L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS4yLjAvZGlzdC9sZWFmbGV0LmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2FqYXguZ29vZ2xlYXBpcy5jb20vYWpheC9saWJzL2pxdWVyeS8xLjExLjEvanF1ZXJ5Lm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvanMvYm9vdHN0cmFwLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvTGVhZmxldC5hd2Vzb21lLW1hcmtlcnMvMi4wLjIvbGVhZmxldC5hd2Vzb21lLW1hcmtlcnMuanMiPjwvc2NyaXB0PgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS4yLjAvZGlzdC9sZWFmbGV0LmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvY3NzL2Jvb3RzdHJhcC10aGVtZS5taW4uY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vZm9udC1hd2Vzb21lLzQuNi4zL2Nzcy9mb250LWF3ZXNvbWUubWluLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9MZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy8yLjAuMi9sZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9yYXdnaXQuY29tL3B5dGhvbi12aXN1YWxpemF0aW9uL2ZvbGl1bS9tYXN0ZXIvZm9saXVtL3RlbXBsYXRlcy9sZWFmbGV0LmF3ZXNvbWUucm90YXRlLmNzcyIvPgogICAgPHN0eWxlPmh0bWwsIGJvZHkge3dpZHRoOiAxMDAlO2hlaWdodDogMTAwJTttYXJnaW46IDA7cGFkZGluZzogMDt9PC9zdHlsZT4KICAgIDxzdHlsZT4jbWFwIHtwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtib3R0b206MDtyaWdodDowO2xlZnQ6MDt9PC9zdHlsZT4KICAgIAogICAgICAgICAgICA8c3R5bGU+ICNtYXBfODIyNTE3NDRkYjc1NGY2ZDhkOTYzMGE1ZTIzY2VmMTkgewogICAgICAgICAgICAgICAgcG9zaXRpb24gOiByZWxhdGl2ZTsKICAgICAgICAgICAgICAgIHdpZHRoIDogMTAwLjAlOwogICAgICAgICAgICAgICAgaGVpZ2h0OiAxMDAuMCU7CiAgICAgICAgICAgICAgICBsZWZ0OiAwLjAlOwogICAgICAgICAgICAgICAgdG9wOiAwLjAlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICA8L3N0eWxlPgogICAgICAgIAo8L2hlYWQ+Cjxib2R5PiAgICAKICAgIAogICAgICAgICAgICA8ZGl2IGNsYXNzPSJmb2xpdW0tbWFwIiBpZD0ibWFwXzgyMjUxNzQ0ZGI3NTRmNmQ4ZDk2MzBhNWUyM2NlZjE5IiA+PC9kaXY+CiAgICAgICAgCjwvYm9keT4KPHNjcmlwdD4gICAgCiAgICAKCiAgICAgICAgICAgIAogICAgICAgICAgICAgICAgdmFyIGJvdW5kcyA9IG51bGw7CiAgICAgICAgICAgIAoKICAgICAgICAgICAgdmFyIG1hcF84MjI1MTc0NGRiNzU0ZjZkOGQ5NjMwYTVlMjNjZWYxOSA9IEwubWFwKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ21hcF84MjI1MTc0NGRiNzU0ZjZkOGQ5NjMwYTVlMjNjZWYxOScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7Y2VudGVyOiBbNDAuNzczNSwtNzMuOTgzNl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6b29tOiAxNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heEJvdW5kczogYm91bmRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXJzOiBbXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmxkQ29weUp1bXA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgCiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHRpbGVfbGF5ZXJfMGNjZTE5OTVkMzE2NGEyZjhjZjg0MDcxMjA3NTJkM2IgPSBMLnRpbGVMYXllcigKICAgICAgICAgICAgICAgICdodHRwczovL3tzfS50aWxlLm9wZW5zdHJlZXRtYXAub3JnL3t6fS97eH0ve3l9LnBuZycsCiAgICAgICAgICAgICAgICB7CiAgImF0dHJpYnV0aW9uIjogbnVsbCwKICAiZGV0ZWN0UmV0aW5hIjogZmFsc2UsCiAgIm1heFpvb20iOiAxOCwKICAibWluWm9vbSI6IDEsCiAgIm5vV3JhcCI6IGZhbHNlLAogICJzdWJkb21haW5zIjogImFiYyIKfQogICAgICAgICAgICAgICAgKS5hZGRUbyhtYXBfODIyNTE3NDRkYjc1NGY2ZDhkOTYzMGE1ZTIzY2VmMTkpOwogICAgICAgIAogICAgCgogICAgICAgICAgICB2YXIgbWFya2VyX2RhMDgyYTg1MmNlYjQ0NDhiNjdiMmNlNmIwNWE1MDRhID0gTC5tYXJrZXIoCiAgICAgICAgICAgICAgICBbNDAuNzcyOSwtNzMuOTgzXSwKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBpY29uOiBuZXcgTC5JY29uLkRlZmF1bHQoKQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIC5hZGRUbyhtYXBfODIyNTE3NDRkYjc1NGY2ZDhkOTYzMGE1ZTIzY2VmMTkpOwogICAgICAgICAgICAKICAgIAogICAgICAgICAgICB2YXIgcG9wdXBfMzM2MTk1OGIxOWI5NGQzNGFiZmQ5NTcwY2NhZjM1ZmUgPSBMLnBvcHVwKHttYXhXaWR0aDogJzMwMCd9KTsKCiAgICAgICAgICAgIAogICAgICAgICAgICAgICAgdmFyIGh0bWxfOWMxODE0NmNjMDI4NDFmZjlkNzk0YTI1NzgwN2ZjNTAgPSAkKCc8ZGl2IGlkPSJodG1sXzljMTgxNDZjYzAyODQxZmY5ZDc5NGEyNTc4MDdmYzUwIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5EYXZpZCBHZWZmZW4gSGFsbDwvZGl2PicpWzBdOwogICAgICAgICAgICAgICAgcG9wdXBfMzM2MTk1OGIxOWI5NGQzNGFiZmQ5NTcwY2NhZjM1ZmUuc2V0Q29udGVudChodG1sXzljMTgxNDZjYzAyODQxZmY5ZDc5NGEyNTc4MDdmYzUwKTsKICAgICAgICAgICAgCgogICAgICAgICAgICBtYXJrZXJfZGEwODJhODUyY2ViNDQ0OGI2N2IyY2U2YjA1YTUwNGEuYmluZFBvcHVwKHBvcHVwXzMzNjE5NThiMTliOTRkMzRhYmZkOTU3MGNjYWYzNWZlKTsKCiAgICAgICAgICAgIAogICAgICAgIAogICAgCgogICAgICAgICAgICB2YXIgbWFya2VyX2I1N2Q2YWVkYzY0OTRhMDE4YjFlMjFjYTI0ZDMyZTkzID0gTC5tYXJrZXIoCiAgICAgICAgICAgICAgICBbNDAuNzc0MSwtNzMuOTg0Ml0sCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgaWNvbjogbmV3IEwuSWNvbi5EZWZhdWx0KCkKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAuYWRkVG8obWFwXzgyMjUxNzQ0ZGI3NTRmNmQ4ZDk2MzBhNWUyM2NlZjE5KTsKICAgICAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHBvcHVwXzNjMjFkMzFhY2Y4YjRlYjNhMWY3ODcyMzRjZDBjYWZmID0gTC5wb3B1cCh7bWF4V2lkdGg6ICczMDAnfSk7CgogICAgICAgICAgICAKICAgICAgICAgICAgICAgIHZhciBodG1sXzk3OWM1Mzk3MDkyZjQwZjY5MjU0YzZkNTQ5NDJhMDM2ID0gJCgnPGRpdiBpZD0iaHRtbF85NzljNTM5NzA5MmY0MGY2OTI1NGM2ZDU0OTQyYTAzNiIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+U3RhbmxleSBILiBLYXBsYW4gUGVudGhvdXNlPC9kaXY+JylbMF07CiAgICAgICAgICAgICAgICBwb3B1cF8zYzIxZDMxYWNmOGI0ZWIzYTFmNzg3MjM0Y2QwY2FmZi5zZXRDb250ZW50KGh0bWxfOTc5YzUzOTcwOTJmNDBmNjkyNTRjNmQ1NDk0MmEwMzYpOwogICAgICAgICAgICAKCiAgICAgICAgICAgIG1hcmtlcl9iNTdkNmFlZGM2NDk0YTAxOGIxZTIxY2EyNGQzMmU5My5iaW5kUG9wdXAocG9wdXBfM2MyMWQzMWFjZjhiNGViM2ExZjc4NzIzNGNkMGNhZmYpOwoKICAgICAgICAgICAgCiAgICAgICAgCjwvc2NyaXB0Pg==\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>" | |
], | |
"text/plain": [ | |
"<folium.folium.Map at 0x1a22aa5940>" | |
] | |
}, | |
"execution_count": 226, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Generate a map\n", | |
"venues = set(df_all[(df_all['Date']==pd.Timestamp(date).date()) & \n", | |
" (df_all['City']==city)]['Venue'])\n", | |
"coordinates = [venue_coordinates[venue] for venue in venues]\n", | |
"\n", | |
"# Find average coordinates to center the map\n", | |
"av_lat, av_lon = np.mean([c[0] for c in coordinates]), np.mean([c[1] for c in coordinates])\n", | |
"\n", | |
"# Create the map\n", | |
"event_map = folium.Map(location=[av_lat, av_lon],\n", | |
" zoom_start=15,\n", | |
"# tiles=\"Stamen Toner\"\n", | |
"# tiles='cartodbpositron'\n", | |
" )\n", | |
"\n", | |
"# Add a marker for each venue\n", | |
"for ind, venue in enumerate(venues):\n", | |
" lat, lon = coordinates[ind]\n", | |
" folium.Marker([lat, lon], popup=venue).add_to(event_map)\n", | |
" \n", | |
"# Display the map\n", | |
"event_map" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Plot\n", | |
"This plot summarizes the preference history of the user. A bar chart representing features of the selected events, along with three most frequently selected composers are presented in this visualization." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 134, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import matplotlib.pyplot as plt\n", | |
"from collections import defaultdict" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Below is a sample group of events selected by the user." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 135, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"selected_events = [12, 45, 47, 51, 100, 104, 107, 150, 151, 156]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 136, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"tags = df_all.iloc[selected_events]['Tags_Simple']" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 137, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df_chart = pd.DataFrame(data={'Chamber': [0, 0, 0, 0, 0], 'Orchestral': [0, 0, 0, 0, 0]}, \n", | |
" index=['Renaissance', 'Baroque', 'Classical', 'Romantic', 'Modern'])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 138, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"for tag in tags:\n", | |
" if 'chamber' in tag:\n", | |
" if 'renaissance' in tag:\n", | |
" df_chart['Chamber'].loc['Renaissance'] += 1\n", | |
" if 'baroque' in tag:\n", | |
" df_chart['Chamber'].loc['Baroque'] += 1\n", | |
" if 'classical' in tag:\n", | |
" df_chart['Chamber'].loc['Classical'] += 1\n", | |
" if 'romantic' in tag:\n", | |
" df_chart['Chamber'].loc['Romantic'] += 1\n", | |
" if 'modern' in tag:\n", | |
" df_chart['Chamber'].loc['Modern'] += 1\n", | |
" elif 'orchestra' in tag:\n", | |
" if 'renaissance' in tag:\n", | |
" df_chart['Orchestral'].loc['Renaissance'] += 1\n", | |
" if 'baroque' in tag:\n", | |
" df_chart['Orchestral'].loc['Baroque'] += 1\n", | |
" if 'classical' in tag:\n", | |
" df_chart['Orchestral'].loc['Classical'] += 1\n", | |
" if 'romantic' in tag:\n", | |
" df_chart['Orchestral'].loc['Romantic'] += 1\n", | |
" if 'modern' in tag:\n", | |
" df_chart['Orchestral'].loc['Modern'] += 1" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 139, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Eliminate rows that are all zeros\n", | |
"df_chart = df_chart.loc[(df_chart!=0).any(axis=1)]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 140, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"tags_w_comp = df_all.iloc[selected_events]['Tags']" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 141, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"composers = defaultdict(int)\n", | |
"for item in tags_w_comp:\n", | |
" for tag in item:\n", | |
" if tag in composer_period.keys():\n", | |
" composers[tag] += 1" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 142, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Pick the top three composers\n", | |
"composers = [x[0] for x in sorted(composers.items(), key=lambda item: item[1], reverse=True)[:3]]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 143, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"/anaconda3/lib/python3.6/site-packages/matplotlib/text.py:2108: UserWarning: You have used the `textcoords` kwarg, but not the `xytext` kwarg. This can lead to surprising results.\n", | |
" warnings.warn(\"You have used the `textcoords` kwarg, but not \"\n" | |
] | |
}, | |
{ | |
"data": { | |
"image/png": "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yF1wwQVu6tSp7t1333WjRo1yN910U3r5YcOGOUnu+++/d845N23aNJcvXz536623ptf9xBNPuFy5crmNGzfGtHXxxRe7atWqZfLdyNzy5cudJPfyyy+7PXv2uD179rjvv//e9ejRw+XMmdMtXrw4wzE75ZRTXPfu3d2sWbPSz6E5c+a4Bx54wL3zzjtu/vz5buTIkS4lJcU1btw4pr2OHTu6IkWKuGrVqrkRI0a4WbNmufbt2ztJ7oMPPshwLKpUqeIGDhzoUlNTXZcuXZwkd99997latWq5iRMnusmTJ7vKlSu7lJQUt3fv3vTXlDNnTnfNNde4mTNnujlz5rhhw4a53r17H/bxAYDj2KFlqkMteKweUUGtahjGikq6VsHIwS2ZlO0Xt36apK+jlq8KyzWKK3e/pD82bNjgnMs81CUnJ7uvv/46fd2GDRtcUlKSe+SRR9LXRYLRoEGDYo769ddf7woUKOA2b97snHOuf//+Lnfu3DH1RcoVL17c7dmzxzm3P9TdcccdMeX27dvnypUr51q3bh2zftKkSU5STKC59tprXYkSJdwvv/wSU7Z58+audu3aGfr+9NNPu6yQWajbu3evK1u2rGvQoEGGEBStdOnSrkSJEm7r1q3p6xYuXOgkuUmTJiXcJy0tze3Zs8eNGDHC5ciRIyb8HSjUzZgxw+XPn9/deOONbt++fellFi9enPD7ef/99ztJbsWKFc65INBIcvPnz08vs379epeUlOSeeeaZmL6vXLky09ccHepGjRrlkpOTY84v54JwmDdvXvevf/0rpq3k5GT31FNPZVp3ZiKhLv6RN29eN27cuJiykWPWqVOng9a7Z88el5qa6iS5r776Kn19x44dMwS4HTt2uEKFCrlbb701w7F49NFH09ft3r3bFSlSxOXOnTs9+DoXvEmQ5BYtWuScc27cuHHOzGLCOQB46JAy1fE0/bpC0h5JmyS9JGm4c+75TMr+O275C0nlo5ZbKZgq+sDMkiMPSbMl5Vy06MCXflWuXFmVK1dOXy5VqpRKlSqldevWZSjboUOHmOVOnTpp+/btWrZsmSRp5syZqlevnlJSUrR37970R8uWLfXrr7/qq69i79Fo1y5mplQ//PCDfvjhB1122WUx69u2bavk5NhLImfOnKk2bdqocOHCGdpaunSptm3bdsC2stoXX3yhH3/8Ud26dZOZHbDsP/7xDxUqtH/27swzz5SkmGO+efNm3XXXXapUqZJy586tnDlzqlu3btq3b5/WrFlz0P688soruuiii3T33Xdr2LBhSkraf/q/9957kqTOnTvH7BNZjmxv2rSpypUrp3HjxsXUm5SUpMsvv1ySVK1aNRUsWFDXXXedXn31Va1fvz7TPj366KO64YYbNHz4cPXu3TtmW9GiRdWpUye9+OKLkTcleumll5ScnKyrr84we33I+vfvr8WLF2vx4sWaOXOmrr76anXt2lVvv/12hrKJzpFdu3bp4YcfVtWqVZUnTx7lzJlTLVq0kCStXLkyw2uoX3//fTX58uVTpUqVEv4stW7dOv15rly5lJKSoho1aqhcuf03CFerVk2S9P3330uSzjrrLCUlJemyyy7TW2+9pV9++eVwDgUAeOV4CnXtJJ0jqY2kOZJ6JLruKrQpbnm3pOjrqkpJqqAgJEY/PpakX3/99YAdKVasWIZ1uXPn1q5duzKsL126dMLlyB/yjRs36r333lPOnDljHpGQFt+XsmXLxiz/+OOPwQsqVSpmfY4cOVSiROzlhhs3btTYsWMztHX33XcfUltZLdJe9B/lzMQf89y5g29n9DHv3LmzRo8erZ49e2rOnDlavHixnnzyyQzlMvPGG2+oYMGCuuqqqzJs27QpOKXKlCkTsz6yHNmelJSkK6+8Um+++WZ6m+PGjVOrVq1UsmRJSVKJEiX07rvvqnjx4urevbvKlSun2rVra8qUKRnanTBhgipWrKi2bdsm7PPNN9+s1atXa+7cuUpLS9PIkSPVoUOHhOfooUpJSVGdOnVUp04dtWzZUi+88IIaNWqkO+64I0PZROfIXXfdpQEDBqhLly6aMWOGPv74Y7322muSMn4fDudnqWjRojHLuXLlSrguup0zzjhDM2bM0K5du3TFFVeodOnSatiwod5///0DHQIA8NLxFOqWOec+cc7NkHSBpK8lPWZm+Y+grl8lrVUQEjM8LrzwwizqsrRhw4aEy6eeeqokqXjx4mrQoEH6yEj8o06dOjH7x49oRf6obty4MWb9vn37MoxKFC9eXJdeemmmbZ1yyikHbCurRULngUaqDtVvv/2mGTNmqHfv3rr11lvVqFEj1alTR3ny5Dn4zqHRo0erUqVKatKkSYaRvUj4iP9+/vTTT5KCYxtx1VVXaevWrZo6dao+//xzffHFFxmCYp06dfTOO+9o06ZNev/993Xqqaeqffv2WrVqVUy5yZMnKy0tTc2aNUs4ynT22WfrnHPO0fDhwzV9+nStW7dO3bt3P+TXfKhq1KihdevWaevWrTHrE50jEydOVLdu3fTPf/5TTZs21TnnnBMzyvpna9GihWbPnq0tW7Zo1qxZ2rt3r9q0aZPhtQCA746nUJfOBR+bcLeCEbceR1DFTEmnSdoeBsWYR/wI19F4/fXYm0Zfe+01FShQQDVr1pQktWrVSitWrFD58uXTR0eiHwULFjxg/eXKlVO5cuX0xhtvxKx/5513tHfv3ph1rVq10ueff64aNWokbCsy+vVnqVmzpk455RSNHDkyffrwSP3+++9yzilnzvQbNOWc08svv3zIdRQtWlRz5sxR2bJl1bhxY61evf9zcRs3Dm4kjYw4RbzyyiuSpEaNGqWvq1Gjhv72t79p3LhxGjdunAoXLqyLLrooYZs5c+ZUgwYN1LdvX+3du1crVqyI2V6hQgXNnz9fO3bsUNOmTfXzzz9nqKNHjx6aPHmyBg4cqDPPPFMNGjQ45Nd8qL744gvly5dP+fMf+D2Uc047d+6M+T5IQWDObnny5FHz5s111113adu2bQmneAHAZ8fT59TFcM5NMbPFknqZ2fPOuZ2HsfsrkrpKmmtmTyj4YNxckk6XdNGOHTuUL1++LOnniBEjlJaWpnPOOUezZs3SyJEj1bdvXxUpUkSS1LNnT02cOFH/+Mc/1LNnT1WtWlU7duzQihUrtHDhQk2ePPmA9SclJalPnz7q1q2brr/+el122WX65ptvNHjwYBUuXDjmurD+/furbt26atSokW655RZVrFhRmzdv1rJly/TNN99o1KhRB309Y8aMUdeuXTVv3jw1adLkqI5Njhw59NRTT6lTp04677zz1K1bNxUvXlxffvmltm3bpgceeOCQ6ypdurT++te/avDgwSpRooSKFCmiF1988bCvoSpcuLBSU1PVsmVLNW7cWPPmzVOVKlV09tlnq127durdu7d27dqlunXrauHChRo0aJC6du2qKlWqxNRz9dVX65577tFHH32kyy67LGbEcNKkSRo/frzatm2rChUq6LffftNTTz2lIkWKqG7duhn6dNppp2nBggVq2rSpmjZtqnfffTdmur1Tp06666679OGHH2rIkCEZ9l+xYoWqV6+uQYMG6b77Dv5JHmvWrFHkutJNmzbprbfe0vz583XnnXdmuE4znpnpvPPO08iRI1WtWjVVrFhRr7/+upYsWXLQdo+FZ599VosXL1arVq1Urlw5/fzzzxo4cKDKly+ffv0dAJwsjsuRuigPKBitu/FwdnLO7ZHUUtIISTdImq4g6F0j6YPIdTlZYfLkyUpNTdVFF12k8ePH64EHHtCDDz6Yvr1w4cL64IMP1KZNGz366KNq2bKlrr32Wk2ePFlNmzY9pDauv/56PfXUU0pNTVXbtm310ksv6ZVXXpGZqXDhwunlypcvr08++US1a9dW79691aJFC910001asGCBmjVrdkht7dixQ1LGawWPVIcOHTRz5kz98ccf6tq1qy6++GKNHj1aFStWPOy63njjDZ155pnq3r27rr32WqWkpOixxx477HoKFSqk2bNnKyUlRU2aNEm/uH/ChAnq2bOnhg8frvPPP1/jxo3TAw88oBdffDFDHZdffrmcc9q4cWOGqddq1aopOTlZffv2VevWrXX99dcrf/78mjt3bqbH9dRTT9X8+fO1d+9eNWnSJH3aVwpGoC644ALlz58/w40c0v7vWfz1gJnp27ev6tevr/r16+uKK67Qp59+quHDhx/ysXzhhRfUqlUr3Xvvvbr88su1d+9ejR079pD2zWp/+9vftGXLFt17770677zzdNttt6l69eqaO3duhtFEAPCdHe202AnqhH/RixcvVt26dTV27NiEF/4fqSuuuEJbtmzR9OnTs6xOHJ0//vhDFStW1Pnnn68RI0Zk2P7ss89q8ODBWrt27Z8+xQ4A+FMc0kXwx+30K/Zbu3athgwZkv6xH8uXL9fAgQOVkpKi9u3bZ2lb7733XobrBJE9tm7dqmXLlunll1/Wxo0b1bNnz4TlFixYoF69ehHoAOAkR6g7AeTNm1fLli3T2LFjtXnzZhUtWlTNmzfX4MGDs+zawIgffvghS+vDkfvwww/VunVrlSlTRkOHDtUZZ5yRsNykSZP+5J4BAI5HTL8CAAAc3w5p+vV4v1ECAAAAh4BQBwAA/nRmpjfffDNb2q5YsaIef/zxbGn7WOKaOgAATiC9h248eKEsNLBHqYMXSmDDhg0aOHCgpk2bph9++EElSpRQrVq1dOutt6pNmzZZ3EtIhDoAAJDFvv32WzVs2FAFCxbUoEGDVLt2baWlpWnu3Lm68cYbvfyPL2lpaXLOKUeOHNnWB6ZfAQBAlurRo4ecc/rkk0/UoUMHVa1aVdWrV9ctt9yipUuXppfbtGmTLrvsMuXPn1+VKlXS+PHjY+q57777VLVqVeXNm1cVK1bUPffco127dqVv79u3r2rWrKmXX35ZFStWVIECBdS1a1f98ccfGjp0qE477TQVL15cd955p9LS0mLq3r59uzp37qwCBQqoTJkyGaZjt27dqhtuuEGlSpVSwYIF1bhxY33yySfp28eMGaMCBQpo+vTpqlmzpnLlyqXly5dn5WE8bIQ6AACQZTZt2qSZM2fqlltuUYECBTJsL1q0aPrz/v37q23btlq6dKk6duyoa6+9Vt9991369vz582vUqFFavny5hg4dqtdee02PPPJITH3ffvutJk+erGnTpmnSpEl644031LZtWy1evFizZ8/WyJEj9dxzz+ntt9+O2e/JJ59U9erVtWTJEvXr10+9e/fWW2+9JSn4P9fnn3++1q9fr2nTpunTTz9Vo0aN1KxZM/3444/pdezatUsDBgzQ8OHD9dVXX6lChQpZcgyPFNOvAAAgy6xevVrOOVWvXv2gZa+66qr0f3/48MMP65lnntHChQvTw1H0v92sWLGievfurccff1wPP/xw+vp9+/Zp9OjRKly4sGrWrKlWrVppwYIFWr9+vXLlyqXq1aurYcOGmjdvXswH9terV0/333+/JKlKlSpavHixnnzySV1yySWaN2+ePvvsM/3888/Kmzdvev+mTp2qcePG6Z577klv+7nnntPZZ599lEctaxDqAABAljmcz7+tVatW+vPk5GSVLFlSGzfuvxHkzTff1NNPP63Vq1dr+/bt2rdvn/bt2xdTR/ny5WP+D3rp0qVVpUoVRf+f99KlS8fUK0n169fPsBwZqfvvf/+r33//XSVLlowps2vXLq1Zsyamz3/9618P+fUea4Q6AACQZSpXriwz0/Lly9WuXbsDls2ZM2fMspmlX/u2aNEiderUSX369NFTTz2lIkWKaMqUKerVq9dB60i0Lj4MHkhaWppKly6thQsXZthWqFCh9Oe5c+fO1hsj4hHqABy6D/pmdw+Obw36ZncPgGxXrFgxtWzZUs8//7xuu+22DNfVbdmyRUWKFDloPe+//75OPfXUmCnY6OvtjtaiRYsyLEemjM866yxt2LBBSUlJqlSpUpa1eaxxowQAAMhSQ4cOlXNOderU0RtvvKGVK1dqxYoVGjZsWMyU64FUqVJF69ev1yuvvKJvvvlGw4YN04QJE7Ksj4sWLdKgQYO0atUqjRgxQmPHjlXPnj0lSc2bN1fDhg3Vtm1bzZgxQ2vXrtWHH36oPn36JBy9O14Q6gAAQJZKSUnRkiVL1KJFC917772qVauWmjVrpilTpmj48OGHVMeFF16ou+++W3fccYdq1aql1NRU9e/fP8v6eOedd+rzzz/X3/72Nz3wwAPq37+/Lr30UknBdO306dPVrFkzdevWTVWrVlWHDh20cuVKnXLKKVnWh6xmh3NBo0dOyhcNHDWmXw+M6VcAx4YdSiFG6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAAJ4wxY8aoQIEC2d2Nw9K3b1/VrFnzmLeTfMxbAAAAWefP/nd9R/jv79avX69+/fpp+vTp2rhxo0qWLKk2bdqoT58+KleuXNb2MYv17dtXb775ppYtW5bdXTksjNQBAIAstXbtWtWpU0fLli3Tyy+/rNWrV2v8+PH68ssvdc455+jbb79NuN8ff/zx53b0KB1v/SXUAQCALHXzzTcrKSlJc+bM0bnnnqvy5curadOmmjNnjpKSknTzzTdLkpo0aaKbbrpJvXr1UsmSJdWwYUNJ0rZt23TTTTepbNmyypMnj6pXr66JEyfGtDF37lzVrFlT+fPnV9OmTbV27dqY7VOnTtXZZ5+tPHnyKCUlRffff39MCHvrrbdUq1Yt5c2bV8WKFVPjxo21YcMGjRkzRv369dOXX34pM5OZacyYMZIkM9OQIUN0ySWXKH/+/Ordu7f27dun6667TikpKcqbN68qV66sf/3rX0pLSzuGRzgxpl8BAECW2bRpk2bOnKkBAwYoX758Mdvy5cunHj166MEHH9TmzZslSePHj9cNN9yghQsXyjkn55xat26tzZs3a/To0apSpYpWrlypXbt2pdeze/duDRo0SKNGjVKePHl0zTXX6MYbb9SsWbMkSbNmzdKVV16pZ555Ro0aNdK6det04403avfu3Xr88cf1008/qVOnTho0aJDat2+v7du3a9GiRZKkjh07atmyZZo2bZrmz58vSSpcuHB62/369dPAgQP1+OOPy8yUlpamU089Va+//rpKliypjz/+WDfccIOKFy+u66677lge6gwIdQAAIMusWrVKzjlVr1494fYzzjhDzjmtWrVKkpSSkqInnngifXtqaqo+/PBDffnll+l1VKpUKaaOvXv3asiQIapataokqVevXuratavS0tKUlJSkRx55RHfffbe6du0qSTr99NP16KOPqnPnznrsscf0v//9T3v27NGll16qChUqSFLMjQwFChRQcnKyypQpk6H/HTt21PXXXx+zrn///unPK1asqCVLlmjChAmEOgAAcOIzs4TrnXMx288+++yY7Z9++qnAXiScAAAXMElEQVTKli2baSiUpNy5c6cHOkk65ZRTtGfPHm3ZskXFihXTf//7X3388cd69NFH08ukpaVp586d+umnn1S7dm01b95cNWvW1HnnnafmzZvr0ksvVcmSJQ/6uurUqZNh3QsvvKCRI0fqu+++086dO7Vnz570sPhn4po6AACQZSpXriwz05dffplw+/Lly2VmOv300yVJ+fPnj9keCX0HkpwcOyYVCYiR69jS0tLUp08fffbZZ+mPzz//XKtWrVLJkiWVI0cOzZ49W7Nnz1atWrX00ksvqXLlylq6dOlB247v78SJE3XHHXeoS5cumjVrlj777DP16NEjW26iYKQOAABkmWLFiqlly5YaOnSoevbsGXNd3e+//64hQ4aodevWKlasWML9zzrrLP34449avnz5AUfrDuSss87SihUr9Je//CXTMmam+vXrq379+nrooYdUo0YNTZw4UbVr11auXLm0b9++Q2rrP//5j+rVq6dbbrklfd2aNWuOqN9Hi5E6AACQpZ5//nnt3btXzZs317vvvqvvv/9e8+fPV4sWLeSc0/PPP5/pvueee67q1aun9u3ba9asWVq7dq1SU1P1zjvvHHL7Dz30kF599VU99NBDWrZsmVasWKE333xT99xzjyRp0aJFGjBggBYvXqx169ZpypQp+v7773XGGWdICq6L++6777RkyRL98ssv2r17d6ZtValSRUuWLNGMGTO0atUqPfzww1qwYMEh9zUrEeoAAECWOv300/XJJ5+oRo0auuqqq1SpUiVdccUVql69uhYvXqyUlJRM901KStKMGTPUsGFDde7cWdWrV9ftt99+WNOZLVu21L///W/NmzdPdevWVd26dTV48GCVL19eUnA36/vvv68LLrhAlStX1l133aUHH3xQnTt3liS1b99ebdq00bnnnquSJUtqwoQJmbbVvXt3dejQQVdccUX6Z/Ddddddh9zXrGSHMnftoZPyRQNH7c/+JPsTzRF+8j4AHETiu07iMFIHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAcIdQAAAB4g1AEAAHiAUAcAAOABQh0AAIAHCHUAAAAeINQBAAB4gFAHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAcIdQAAAB4g1AEAAHiAUAcAAOABQh0AAIAHCHUAAAAeINQBAAB4gFAHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAcIdQAAAB4g1AEAAHiAUAcAAOABQh0AAIAHCHUAAAAeINQBAAB4gFAHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAcIdQAAAB4g1AEAAHiAUAcAAOABQh0AAIAHCHUAAAAeINQBAAB4gFAHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAcIdQAAAB4g1AEAAHiAUAcAAOABQh0AAIAHCHUAAAAeINQBAAB4gFAHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAcIdQAAAB4g1AEAAHiAUAcAAOABQh0AAIAHCHUAAAAeINQBAAB4gFAHAADgAUIdAACABwh1AAAAHiDUAQAAeIBQBwAA4AFCHQAAgAeSs7sDAOCL3kM3ZncXjnsDe5TK7i4A3mKkDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8kJzdHfDaB32zuwfHtwZ9s7sHAAB4g5E6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEOAADAA4Q6AAAADxDqAAAAPECoAwAA8AChDgAAwAOEOgAAAA8Q6gAAADxAqAMAAPAAoQ4AAMADhDoAAAAPEOoAAAA8QKgDAADwAKEO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\n", | |
"text/plain": [ | |
"<matplotlib.figure.Figure at 0x1a17c05dd8>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"plt.figure(figsize=(10,8))\n", | |
"pos_1 = np.arange(len(df_chart.index))\n", | |
"plt.bar(pos_1, df_chart['Chamber'], align='edge', linewidth=0, width=0.4,\n", | |
" color='royalblue', alpha=0.7, label='Chamber')\n", | |
"pos_2 = np.arange(len(df_chart.index))-0.4\n", | |
"plt.bar(pos_2, df_chart['Orchestral'], align='edge', width=0.4, \n", | |
" color='darkorange', alpha=0.5, label='Orchestral')\n", | |
"plt.legend(loc='best', frameon=False, title=None, fontsize=14)\n", | |
"\n", | |
"plt.tick_params(top='off', bottom='off', left='off', \n", | |
" right='off', labelleft='off', labelbottom='on')\n", | |
"\n", | |
"ax = plt.gca()\n", | |
"ax.annotate('Favorites: \\n', (0.05, 0.9), fontsize=16, \n", | |
" textcoords='figure fraction', fontweight='bold')\n", | |
"ax.annotate(composers[0].title() + ', ' + composers[1].title() + ', ' + composers[2].title(), \n", | |
" (0.05, 0.89), fontsize=16, textcoords='figure fraction')\n", | |
"\n", | |
"for spine in plt.gca().spines.values():\n", | |
" spine.set_visible(False)\n", | |
"\n", | |
"plt.xticks(pos_1, df_chart.index, alpha=1, fontsize=16);" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"%who" | |
] | |
} | |
], | |
"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