├── PlotEllipse ├── __init__.py ├── plot_ellipse.png ├── README.md └── plot_ellipse.py ├── .gitignore ├── PlottingWithPandas ├── README.md └── PlottingWithPandas_DatesAndBarPlots.ipynb ├── BoundedClustering └── README.md ├── README.md ├── YATS_YetAnotherTSPSolution ├── README.md └── YetAnotherTSPSolution.ipynb ├── GettingToKnowTheMelSpectrogram ├── README.md └── Haunting_song_of_humpback_whales-youtube-W5Trznre92c.wav ├── SolvingTSPUsingDynamicProgramming └── README.md └── SetTSP ├── README.md └── SetTSP.ipynb /PlotEllipse/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */.ipynb_checkpoints/* 2 | .idea/ -------------------------------------------------------------------------------- /PlotEllipse/plot_ellipse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DalyaG/CodeSnippetsForPosterity/HEAD/PlotEllipse/plot_ellipse.png -------------------------------------------------------------------------------- /PlottingWithPandas/README.md: -------------------------------------------------------------------------------- 1 | # Plotting With Pandas - Dates and Bar Plots 2 | 3 | This notebook accompanies a 4 | [blog post]() 5 | by the same name. -------------------------------------------------------------------------------- /BoundedClustering/README.md: -------------------------------------------------------------------------------- 1 | # Bounded Clustering 2 | 3 | This notebook accompanies a 4 | [blog post](http://bit.ly/BoundedClustering) 5 | by the same name. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeSnippetsForPosterity 2 | My Dream is that each one of these code snippets will become a blog post. So let's take this dream one snippet at a time :) 3 | -------------------------------------------------------------------------------- /YATS_YetAnotherTSPSolution/README.md: -------------------------------------------------------------------------------- 1 | # YATS - Yet Another TSP Solution 2 | 3 | This notebook accompanies a 4 | [blog post](https://medium.com/hackernoon/yats-yet-another-tsp-solution-6a71aeabe1f8) 5 | by the same name. -------------------------------------------------------------------------------- /GettingToKnowTheMelSpectrogram/README.md: -------------------------------------------------------------------------------- 1 | # Getting to Know the Mel Spectrogram 2 | 3 | This notebook accompanies a 4 | [blog post](https://towardsdatascience.com/getting-to-know-the-mel-spectrogram-31bca3e2d9d0) 5 | by the same name. -------------------------------------------------------------------------------- /GettingToKnowTheMelSpectrogram/Haunting_song_of_humpback_whales-youtube-W5Trznre92c.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DalyaG/CodeSnippetsForPosterity/HEAD/GettingToKnowTheMelSpectrogram/Haunting_song_of_humpback_whales-youtube-W5Trznre92c.wav -------------------------------------------------------------------------------- /SolvingTSPUsingDynamicProgramming/README.md: -------------------------------------------------------------------------------- 1 | # Solving TSP Using Dynamic Programming 2 | 3 | This notebook accompanies a 4 | [blog post](https://towardsdatascience.com/solving-tsp-using-dynamic-programming-2c77da86610d) 5 | by the same name. -------------------------------------------------------------------------------- /SetTSP/README.md: -------------------------------------------------------------------------------- 1 | # Set-TSP - Because There Is More Than One Place to Get Bread 2 | 3 | This notebook accompanies a 4 | [blog post](https://towardsdatascience.com/set-tsp-because-there-is-more-than-one-place-to-get-bread-712fdb5b381) 5 | by the same name. -------------------------------------------------------------------------------- /PlotEllipse/README.md: -------------------------------------------------------------------------------- 1 | # PlotEllipse 2 | Plot an ellipse in s2map around two GPS locations. 3 | 4 | For the background story and some fun math stuff, you can read 5 | [this blog post](https://medium.freecodecamp.org/a-total-ellipse-on-the-map-9e30d5235078) 6 | on Free Code Camp. 7 | 8 | Example usage: 9 | 10 | > python plot_ellipse.py --p1_lat 32.076761 --p1_lng 34.792510 --p2_lat 32.083257 --p2_lng 34.767737 -r 3000 11 | 12 | 13 | And a tab in your browser will open with the following plot: 14 | 15 | ![When developers want to do something fun outside and they end up writing a script about it instead.](../../master/PlotEllipse/plot_ellipse.png) 16 | 17 | -------------------------------------------------------------------------------- /PlotEllipse/plot_ellipse.py: -------------------------------------------------------------------------------- 1 | 2 | import subprocess 3 | import numpy as np 4 | from haversine import haversine 5 | from optparse import OptionParser 6 | from math import sqrt, pow, atan2, degrees, pi 7 | from shapely import affinity 8 | from shapely.geometry import LineString 9 | 10 | R_EARTH = 6371000 11 | 12 | def Main(): 13 | """ 14 | Input 2 lat-lng points and a joint-radius, to draw an ellipse around these two centers. 15 | NOTE: 16 | The joint-radius should be at least as large at the distance between the two points. 17 | 18 | Example command: 19 | 20 | python plot_ellipse.py --p1_lat 32.076761 --p1_lng 34.792510 --p2_lat 32.083257 --p2_lng 34.767737 -r 3000 21 | 22 | In details: 23 | 1. From the lat-lng get ellipse parameters. 24 | 2. Draw ellipse around the origin (0,0) measured in meters. 25 | 3. Move this ellipse to be centered around the input centers. 26 | 4. Open browser tab with ellipse in s2map. 27 | """ 28 | parser = OptionParser() 29 | parser.add_option("--p1_lat") 30 | parser.add_option("--p1_lng") 31 | parser.add_option("--p2_lat") 32 | parser.add_option("--p2_lng") 33 | parser.add_option("-r", "--radius_in_meters") 34 | parser.add_option("-n", "--num_points") 35 | options, _ = parser.parse_args() 36 | 37 | num_points = int(options.num_points) if options.num_points is not None else 20 38 | p1_lat, p1_lng = float(options.p1_lat), float(options.p1_lng) 39 | p2_lat, p2_lng = float(options.p2_lat), float(options.p2_lng) 40 | radius_in_meters = float(options.radius_in_meters) 41 | 42 | a, b = GetEllipseAxisLengths(p1_lat, p1_lng, p2_lat, p2_lng, radius_in_meters) 43 | perimeter_points_in_meters = GetEllipsePointInMeters(a, b, num_points) 44 | points = GetEllipsePoints(p1_lat, p1_lng, p2_lat, p2_lng, perimeter_points_in_meters) 45 | OpenS2Map(points) 46 | 47 | 48 | def GetEllipseAxisLengths(p1_lat, p1_lng, p2_lat, p2_lng, radius_in_meters): 49 | d = haversine((p1_lat, p1_lng), (p2_lat, p2_lng)) * 1000.0 50 | if radius_in_meters < d: 51 | raise ValueError("Please specify radius larger than the distance between the two input points.") 52 | a = radius_in_meters / 2.0 53 | b = sqrt(pow(a, 2) - pow(d / 2.0, 2)) 54 | return a, b 55 | 56 | def GetEllipsePointInMeters(a, b, num_points): 57 | """ 58 | :param a: length of "horizontal" axis in meters 59 | :param b: length of "vertical" axis in meters 60 | :param num_points: number of points to draw on each side of the ellipse 61 | :return: List of tuples of perimeter points on the ellipse, centered around (0,0), in m. 62 | """ 63 | x_points = list(np.linspace(-a, a, num_points))[1:-1] 64 | y_points_pos = [sqrt(pow(a, 2) - pow(x, 2)) * (float(b) / float(a)) 65 | for x in x_points] 66 | y_points_neg = [-y for y in y_points_pos] 67 | 68 | perimeter_points_in_meters = [tuple([-a, 0])] + \ 69 | [tuple([x, y]) for x, y in zip(x_points, y_points_pos)] + \ 70 | [tuple([a, 0])] + \ 71 | list(reversed([tuple([x, y]) for x, y in zip(x_points, y_points_neg)])) 72 | return perimeter_points_in_meters 73 | 74 | def GetEllipsePoints(p1_lat, p1_lng, p2_lat, p2_lng, perimeter_points_in_meters): 75 | """ 76 | Enter ellipse centers in lat-lng and ellipse perimeter points around the origin (0,0), 77 | and get points on the perimeter of the ellipse around the centers in lat-lng. 78 | :param p1_lat: lat coordinates of center point 1 79 | :param p1_lng: lng coordinates of center point 1 80 | :param p2_lat: lat coordinates of center point 2 81 | :param p2_lng: lng coordinates of center point 2 82 | :param perimeter_points_in_meters: List of tuples of perimeter points on the ellipse, centered around (0,0), in m. 83 | :return: 84 | """ 85 | center_lng = (p1_lng + p2_lng) / 2.0 86 | center_lat = (p1_lat + p2_lat) / 2.0 87 | perimeter_points_in_lng_lat = [AddMetersToPoint(center_lng, center_lat, p[0], p[1]) 88 | for p in perimeter_points_in_meters] 89 | ellipse = LineString(perimeter_points_in_lng_lat) 90 | 91 | angle = degrees(atan2(p2_lat - p1_lat, p2_lng - p1_lng)) 92 | ellipse_rotated = affinity.rotate(ellipse, angle) 93 | 94 | ellipse_points_lng_lat = list(ellipse_rotated.coords) 95 | ellipse_points = [tuple([p[1], p[0]]) for p in ellipse_points_lng_lat] 96 | return ellipse_points 97 | 98 | def AddMetersToPoint(center_lng, center_lat, dx, dy): 99 | """ 100 | :param center_lng, center_lat: GPS coordinates of the center between the two input points. 101 | :param dx: distance to add to x-axis (lng) in meters 102 | :param dy: distance to add to y-axis (lat) in meters 103 | """ 104 | new_x = center_lng + (dx / R_EARTH) * (180 / pi) / np.cos(center_lat * pi/180) 105 | new_y = center_lat + (dy / R_EARTH) * (180 / pi) 106 | return tuple([new_x, new_y]) 107 | 108 | def OpenS2Map(points): 109 | url = "http://s2map.com/#order=latlng&mode=polygon&s2=false&points={}".format(str(points).replace(" ", ",")) 110 | cmd = ["python", "-m", "webbrowser", "-t", url] 111 | subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() 112 | 113 | 114 | if __name__ == '__main__': 115 | Main() 116 | -------------------------------------------------------------------------------- /YATS_YetAnotherTSPSolution/YetAnotherTSPSolution.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# YATS - Yet Another TSP Solution\n", 8 | "\n", 9 | "## This notebook was created to serve a [blog post](https://medium.com/hackernoon/yats-yet-another-tsp-solution-6a71aeabe1f8) by the same name." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "# written in python 3.7.3\n", 21 | "import numpy as np\n", 22 | "import math\n", 23 | "from geojson import LineString, Point, Feature, FeatureCollection\n", 24 | "import geojsonio\n", 25 | "import json\n", 26 | "import random\n", 27 | "random_seed = 42\n", 28 | "random.seed(random_seed)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "# Part I - The Code For Our Solution" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "### The Geographic Building Blocks" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": { 49 | "collapsed": true 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "class GeoPoint:\n", 54 | " def __init__(self, lng: float, lat: float):\n", 55 | " # Why 5 digits? According to https://en.wikipedia.org/wiki/Decimal_degrees it's 1m. accuracy.\n", 56 | " self.lng = round(lng, 5)\n", 57 | " self.lat = round(lat, 5)\n", 58 | " \n", 59 | " def __repr__(self):\n", 60 | " # copy-pastable format to most map applications\n", 61 | " return f\"[{self.lng}, {self.lat}]\"\n", 62 | " \n", 63 | "def euclidean_dist(geo_point_1: GeoPoint, geo_point_2: GeoPoint) -> float:\n", 64 | " d = np.linalg.norm(np.array([geo_point_1.lat, geo_point_1.lng])\n", 65 | " - np.array([geo_point_2.lat, geo_point_2.lng]))\n", 66 | " return d\n", 67 | "\n", 68 | "def get_geo_point_of_center(geo_points: [GeoPoint]) -> GeoPoint:\n", 69 | " lng_list, lat_list = list(zip(*[[g.lng, g.lat] for g in geo_points]))\n", 70 | " lng_center, lat_center = np.mean(np.array([lng_list, lat_list]), axis=1)\n", 71 | " return GeoPoint(lng_center, lat_center)\n", 72 | "\n", 73 | "def get_angle_from_reference_geo_point_in_deg(reference_geo_point: GeoPoint, other_geo_point: GeoPoint) -> float:\n", 74 | " x = other_geo_point.lng - reference_geo_point.lng\n", 75 | " y = other_geo_point.lat - reference_geo_point.lat\n", 76 | " angle_from_reference_in_deg = math.degrees(math.atan2(y, x))\n", 77 | " return angle_from_reference_in_deg" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "### Our Initial Route - An Angular Route" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 3, 90 | "metadata": { 91 | "collapsed": true 92 | }, 93 | "outputs": [], 94 | "source": [ 95 | "def get_angular_route(geo_points: [GeoPoint]) -> [int]:\n", 96 | " center = get_geo_point_of_center(geo_points)\n", 97 | " route_idxs = sorted(list(range(len(geo_points))), \n", 98 | " key=lambda i: \n", 99 | " get_angle_from_reference_geo_point_in_deg(center, geo_points[i]),\n", 100 | " reverse=True)\n", 101 | " return route_idxs" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "### Visualizing" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 4, 114 | "metadata": { 115 | "collapsed": true 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "# opens a new tab with the route visualization on geojson.io\n", 120 | "def visualize(route_idxs: [int], geo_points: [GeoPoint]):\n", 121 | " lng_lat_list = [tuple([geo_points[i].lng, geo_points[i].lat])\n", 122 | " for i in route_idxs]\n", 123 | " route = Feature(geometry=LineString(lng_lat_list),\n", 124 | " properties={\"name\": \"This is our route\",\n", 125 | " \"stroke\": \"#8B0000\"})\n", 126 | " places = [Feature(geometry=Point(lng_lat), \n", 127 | " properties={\"name\": f\"Place {route_idxs[i]}\",\n", 128 | " \"marker-symbol\": int(str(i)[-1]),\n", 129 | " \"marker-color\": \"#00008B\"})\n", 130 | " for i, lng_lat in enumerate(lng_lat_list)]\n", 131 | " \n", 132 | " feature_collection = FeatureCollection(features=[route] + places)\n", 133 | " geojsonio.display(json.dumps(feature_collection));" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "### Optimization step" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 5, 146 | "metadata": { 147 | "collapsed": true 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "def get_route_len(distances_array: np.array, route_idxs):\n", 152 | " route_len = sum([distances_array[i1][i2]\n", 153 | " for i1, i2 in zip(route_idxs[:-1], route_idxs[1:])])\n", 154 | " return route_len\n", 155 | "\n", 156 | "def optimize_route(distances_array: np.array, route_idxs: [int], n_iter: int) -> [int]:\n", 157 | " prev_cost = get_route_len(distances_array, route_idxs)\n", 158 | " \n", 159 | " all_idxs = list(range(len(route_idxs)))\n", 160 | " for _ in range(n_iter): \n", 161 | " i1, i2 = random.sample(all_idxs, 2)\n", 162 | " route_idxs[i2], route_idxs[i1] = route_idxs[i1], route_idxs[i2]\n", 163 | " new_cost = get_route_len(distances_array, route_idxs)\n", 164 | " if new_cost < prev_cost:\n", 165 | " prev_cost = new_cost\n", 166 | " else:\n", 167 | " route_idxs[i2], route_idxs[i1] = route_idxs[i1], route_idxs[i2]\n", 168 | " return route_idxs" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "### Wrap It All Up" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 6, 181 | "metadata": { 182 | "collapsed": true 183 | }, 184 | "outputs": [], 185 | "source": [ 186 | "# opens a new tab with the route visualization on geojson.io\n", 187 | "def plot_best_route(geo_points: [GeoPoint], distances_array: np.array, n_iter: int) -> None:\n", 188 | " route_idxs = get_angular_route(geo_points)\n", 189 | " route_idxs = optimize_route(distances_array, route_idxs, n_iter)\n", 190 | " visualize(route_idxs, geo_points)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "# Part II - My Friday Morning Errands" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 7, 203 | "metadata": { 204 | "collapsed": false 205 | }, 206 | "outputs": [], 207 | "source": [ 208 | "rice = GeoPoint(34.904145, 32.178397)\n", 209 | "veg = GeoPoint(34.899660, 32.178243)\n", 210 | "pet= GeoPoint(34.899918, 32.177080)\n", 211 | "garden = GeoPoint(34.904370, 32.173966)\n", 212 | "pharm = GeoPoint(34.909027, 32.177480)\n", 213 | "pasta = GeoPoint(34.906774, 32.178279)\n", 214 | "pita = GeoPoint(34.903383, 32.177381)\n", 215 | "\n", 216 | "geo_points = [rice, veg, pet, garden, pharm, pasta, pita]" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 8, 222 | "metadata": { 223 | "collapsed": false 224 | }, 225 | "outputs": [ 226 | { 227 | "data": { 228 | "text/plain": [ 229 | "array([[ 0, 600, 550, 600, 650, 400, 150],\n", 230 | " [ 600, 0, 170, 900, 1100, 850, 550],\n", 231 | " [ 550, 170, 0, 750, 1000, 850, 400],\n", 232 | " [ 600, 900, 750, 0, 900, 750, 600],\n", 233 | " [ 650, 1100, 1000, 900, 0, 260, 700],\n", 234 | " [ 400, 850, 850, 750, 260, 0, 500],\n", 235 | " [ 150, 550, 400, 600, 700, 500, 0]])" 236 | ] 237 | }, 238 | "execution_count": 8, 239 | "metadata": {}, 240 | "output_type": "execute_result" 241 | } 242 | ], 243 | "source": [ 244 | "distances_array = np.array([[0, 600, 550, 600, 650, 400, 150], \n", 245 | " [0, 0, 170, 900, 1100, 850, 550], \n", 246 | " [0, 0, 0, 750, 1000, 850, 400], \n", 247 | " [0, 0, 0, 0, 900, 750, 600], \n", 248 | " [0, 0, 0, 0, 0, 260, 700], \n", 249 | " [0, 0, 0, 0, 0, 0, 500], \n", 250 | " [0] * 7])\n", 251 | "distances_array += distances_array.transpose()\n", 252 | "distances_array" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 9, 258 | "metadata": { 259 | "collapsed": true 260 | }, 261 | "outputs": [], 262 | "source": [ 263 | "# opens a new tab with the route visualization on geojson.io\n", 264 | "plot_best_route(geo_points, distances_array, 100)" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "# Part III - A Larger Experiment" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 10, 277 | "metadata": { 278 | "collapsed": true 279 | }, 280 | "outputs": [], 281 | "source": [ 282 | "def euclidean_dist(geo_point_1: GeoPoint, geo_point_2: GeoPoint):\n", 283 | " d = np.linalg.norm(np.array([geo_point_1.lat, geo_point_1.lng])\n", 284 | " - np.array([geo_point_2.lat, geo_point_2.lng]))\n", 285 | " return d" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 11, 291 | "metadata": { 292 | "collapsed": false 293 | }, 294 | "outputs": [], 295 | "source": [ 296 | "n_points = 50\n", 297 | "geo_points = [GeoPoint(34.8 + (0.1 * random.random()), 32.1 + (0.1 * random.random()))\n", 298 | " for _ in range(n_points)]\n", 299 | "distances_array = np.array([[euclidean_dist(g_from, g_to)\n", 300 | " for g_to in geo_points]\n", 301 | " for g_from in geo_points])" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 12, 307 | "metadata": { 308 | "collapsed": false 309 | }, 310 | "outputs": [ 311 | { 312 | "name": "stdout", 313 | "output_type": "stream", 314 | "text": [ 315 | "Route length for random order: 2.8800609543466313\n", 316 | "Initial route length: 0.9013753942969249\n", 317 | "Final route length: 0.6142705755858876\n", 318 | "Wall time: 304 ms\n" 319 | ] 320 | } 321 | ], 322 | "source": [ 323 | "%%time\n", 324 | "\n", 325 | "n_iter = 10000\n", 326 | "random_cost = get_route_len(distances_array, list(range(n_points)))\n", 327 | "print(f\"Route length for random order: {random_cost}\")\n", 328 | "route_idxs = get_angular_route(geo_points)\n", 329 | "first_cost = get_route_len(distances_array, route_idxs)\n", 330 | "print(f\"Initial route length: {first_cost}\")\n", 331 | "route_idxs = optimize_route(distances_array, route_idxs, n_iter)\n", 332 | "last_cost = get_route_len(distances_array, route_idxs)\n", 333 | "print(f\"Final route length: {last_cost}\")\n", 334 | "visualize(route_idxs, geo_points)" 335 | ] 336 | } 337 | ], 338 | "metadata": { 339 | "anaconda-cloud": {}, 340 | "kernelspec": { 341 | "display_name": "quay_rnd", 342 | "language": "python", 343 | "name": "quay_rnd" 344 | }, 345 | "language_info": { 346 | "codemirror_mode": { 347 | "name": "ipython", 348 | "version": 3 349 | }, 350 | "file_extension": ".py", 351 | "mimetype": "text/x-python", 352 | "name": "python", 353 | "nbconvert_exporter": "python", 354 | "pygments_lexer": "ipython3", 355 | "version": "3.7.3" 356 | } 357 | }, 358 | "nbformat": 4, 359 | "nbformat_minor": 1 360 | } 361 | -------------------------------------------------------------------------------- /SetTSP/SetTSP.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Set-TSP - Because There Is More Than One Place to Get Bread\n", 8 | "\n", 9 | "## This notebook was created to serve a [blog post](https://towardsdatascience.com/set-tsp-because-there-is-more-than-one-place-to-get-bread-712fdb5b381) by the same name." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "# written in python 3.7.3\n", 21 | "import json\n", 22 | "import random\n", 23 | "\n", 24 | "import geojsonio\n", 25 | "import numpy as np\n", 26 | "import pandas as pd\n", 27 | "from geojson import LineString, Point, Feature, FeatureCollection\n", 28 | "from math import cos, asin, sqrt, degrees, atan2\n", 29 | "\n", 30 | "import matplotlib.pyplot as plt\n", 31 | "%matplotlib inline\n", 32 | "\n", 33 | "random_seed = 42\n", 34 | "random.seed(random_seed)" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "# Part I - The Geographic Building Blocks\n", 42 | "\n", 43 | "### For more information on Haversine formula, go to [this](https://en.wikipedia.org/wiki/Haversine_formula) wiki page, \n", 44 | "\n", 45 | "### and for the specific formulation used here go to [this](https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula/21623206#21623206) Stackoverflow answer" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 2, 51 | "metadata": { 52 | "collapsed": false 53 | }, 54 | "outputs": [], 55 | "source": [ 56 | "p = 0.017453292519943295 # Pi / 180\n", 57 | "r = 12742000 # Earth's radius is ~6371km => r = 2 * Earth's radius\n", 58 | "\n", 59 | "class GeoPoint:\n", 60 | " def __init__(self, lng: float, lat: float, name_: str = None):\n", 61 | " # Why 5 digits? According to https://en.wikipedia.org/wiki/Decimal_degrees it's 1m. accuracy.\n", 62 | " self.lng = round(lng, 5)\n", 63 | " self.lat = round(lat, 5)\n", 64 | " self.name_ = name_\n", 65 | " \n", 66 | " def __repr__(self):\n", 67 | " # Copy-pastable format for most map applications\n", 68 | " name_str = f\"{self.name_}, \" if self.name_ is not None else \"\"\n", 69 | " return f\"[{name_str}{self.lat}, {self.lng}]\"\n", 70 | " \n", 71 | " def get_dist_from(self, other) -> int:\n", 72 | " # Return non-euclidean distance in meters, using Haversine formula\n", 73 | " # For more information on this formulation go to \n", 74 | " a = (0.5 \n", 75 | " - cos((other.lat - self.lat) * p)/2 \n", 76 | " + (cos(self.lat * p) \n", 77 | " * cos(other.lat * p) \n", 78 | " * (1 - cos((other.lng - self.lng) * p)) / 2))\n", 79 | " d = int(r * asin(sqrt(a))) \n", 80 | " return d" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 3, 86 | "metadata": { 87 | "collapsed": false 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "def get_all_geopoints_from_all_sets(all_sets):\n", 92 | " geo_points = [g\n", 93 | " for set_ in all_sets\n", 94 | " for g in set_]\n", 95 | " return geo_points" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 4, 101 | "metadata": { 102 | "collapsed": false 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "def get_distances_array_and_set_to_points_dict(all_sets):\n", 107 | " all_geo_points = []\n", 108 | " set_to_points_dict = {}\n", 109 | " first_point_idx = 0\n", 110 | " for idx, set_ in enumerate(all_sets):\n", 111 | " all_geo_points += set_\n", 112 | " n_points_in_set = len(set_)\n", 113 | " set_to_points_dict[idx] = list(range(first_point_idx, first_point_idx + n_points_in_set))\n", 114 | " first_point_idx += n_points_in_set\n", 115 | "\n", 116 | " n_points = first_point_idx\n", 117 | " distances_array = np.array([[all_geo_points[i].get_dist_from(all_geo_points[j])\n", 118 | " for i in range(n_points)]\n", 119 | " for j in range(n_points)])\n", 120 | "\n", 121 | " return all_geo_points, set_to_points_dict, distances_array" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 5, 127 | "metadata": { 128 | "collapsed": false 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "bread_0 = GeoPoint(lat=32.178500, lng=34.906531, name_=\"bread_0\")\n", 133 | "bread_1 = GeoPoint(lat=32.175431, lng=34.907089, name_=\"bread_1\")\n", 134 | "bread_2 = GeoPoint(lat=32.175041, lng=34.898474, name_=\"bread_2\")\n", 135 | "bread_set = [bread_0, bread_1, bread_2]\n", 136 | "\n", 137 | "veg_0 = GeoPoint(lat=32.178192, lng=34.899633, name_=\"veg_0\")\n", 138 | "veg_1 = GeoPoint(lat=32.176376, lng=34.902369, name_=\"veg_1\")\n", 139 | "veg_2 = GeoPoint(lat=32.174051, lng=34.899397, name_=\"veg_2\")\n", 140 | "veg_set = [veg_0, veg_1, veg_2]\n", 141 | "\n", 142 | "beer_0 = GeoPoint(lat=32.177774, lng=34.907175, name_=\"beer_0\")\n", 143 | "beer_1 = GeoPoint(lat=32.177102, lng=34.899719, name_=\"beer_1\")\n", 144 | "beer_set = [beer_0, beer_1]\n", 145 | "\n", 146 | "all_sets = [bread_set, veg_set, beer_set]" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 6, 152 | "metadata": { 153 | "collapsed": false 154 | }, 155 | "outputs": [ 156 | { 157 | "name": "stdout", 158 | "output_type": "stream", 159 | "text": [ 160 | "[[bread_0, 32.1785, 34.90653], [bread_1, 32.17543, 34.90709], [bread_2, 32.17504, 34.89847], [veg_0, 32.17819, 34.89963], [veg_1, 32.17638, 34.90237], [veg_2, 32.17405, 34.8994], [beer_0, 32.17777, 34.90718], [beer_1, 32.1771, 34.89972]]\n", 161 | "{0: [0, 1, 2], 1: [3, 4, 5], 2: [6, 7]}\n" 162 | ] 163 | }, 164 | { 165 | "data": { 166 | "text/plain": [ 167 | "array([[ 0, 345, 850, 650, 457, 833, 101, 659],\n", 168 | " [345, 0, 812, 766, 456, 739, 260, 718],\n", 169 | " [850, 812, 0, 366, 396, 140, 874, 257],\n", 170 | " [650, 766, 366, 0, 327, 460, 712, 121],\n", 171 | " [457, 456, 396, 327, 0, 381, 478, 261],\n", 172 | " [833, 739, 140, 460, 381, 0, 840, 340],\n", 173 | " [101, 260, 874, 712, 478, 840, 0, 706],\n", 174 | " [659, 718, 257, 121, 261, 340, 706, 0]])" 175 | ] 176 | }, 177 | "execution_count": 6, 178 | "metadata": {}, 179 | "output_type": "execute_result" 180 | } 181 | ], 182 | "source": [ 183 | "all_geo_points, set_to_points_dict, distances_array = get_distances_array_and_set_to_points_dict(all_sets)\n", 184 | "\n", 185 | "print(all_geo_points)\n", 186 | "print(set_to_points_dict)\n", 187 | "distances_array" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "# Part II - Solving Using Dynammic Programming" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 7, 200 | "metadata": { 201 | "collapsed": true 202 | }, 203 | "outputs": [], 204 | "source": [ 205 | "def DP_Set_TSP(set_to_points_dict, distances_array):\n", 206 | " all_sets = set(set_to_points_dict.keys())\n", 207 | " n_sets = len(all_sets)\n", 208 | "\n", 209 | " # memo keys: tuple(sorted_sets_in_path, last_set_in_path, last_point_in_path)\n", 210 | " # memo values: tuple(cost_thus_far, next_to_last_set_in_path, next_to_last_point_in_path)\n", 211 | " memo = {(tuple([set_idx]), set_idx, p_idx): tuple([0, None, None])\n", 212 | " for set_idx, points_idxs in set_to_points_dict.items()\n", 213 | " for p_idx in points_idxs}\n", 214 | " queue = [(tuple([set_idx]), set_idx, p_idx)\n", 215 | " for set_idx, points_idxs in set_to_points_dict.items()\n", 216 | " for p_idx in points_idxs]\n", 217 | "\n", 218 | " while queue:\n", 219 | " prev_visited_sets, prev_last_set, prev_last_point = queue.pop(0)\n", 220 | " prev_dist, _, _ = memo[(prev_visited_sets, prev_last_set, prev_last_point)]\n", 221 | "\n", 222 | " to_visit = all_sets.difference(set(prev_visited_sets))\n", 223 | " for new_last_set in to_visit:\n", 224 | " new_visited_sets = tuple(sorted(list(prev_visited_sets) + [new_last_set]))\n", 225 | " for new_last_point in set_to_points_dict[new_last_set]:\n", 226 | " new_dist = prev_dist + distances_array[prev_last_point][new_last_point]\n", 227 | "\n", 228 | " new_key = (new_visited_sets, new_last_set, new_last_point)\n", 229 | " new_value = (new_dist, prev_last_set, prev_last_point)\n", 230 | "\n", 231 | " if new_key not in memo:\n", 232 | " memo[new_key] = new_value\n", 233 | " queue += [new_key]\n", 234 | " else:\n", 235 | " if new_dist < memo[new_key][0]:\n", 236 | " memo[new_key] = new_value\n", 237 | "\n", 238 | " optimal_path_in_points_idxs, optimal_path_in_sets_idxs, optimal_cost = retrace_optimal_path(memo, n_sets)\n", 239 | "\n", 240 | " return optimal_path_in_points_idxs, optimal_path_in_sets_idxs, optimal_cost" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 8, 246 | "metadata": { 247 | "collapsed": true 248 | }, 249 | "outputs": [], 250 | "source": [ 251 | "def retrace_optimal_path(memo: dict, n_sets: int) -> [[int], [int], float]:\n", 252 | " sets_to_retrace = tuple(range(n_sets))\n", 253 | "\n", 254 | " full_path_memo = dict((k, v) for k, v in memo.items() if k[0] == sets_to_retrace)\n", 255 | " path_key = min(full_path_memo.keys(), key=lambda x: full_path_memo[x][0])\n", 256 | "\n", 257 | " _, last_set, last_point = path_key\n", 258 | " optimal_cost, next_to_last_set, next_to_last_point = memo[path_key]\n", 259 | "\n", 260 | " optimal_path_in_points_idxs = [last_point]\n", 261 | " optimal_path_in_sets_idxs = [last_set]\n", 262 | " sets_to_retrace = tuple(sorted(set(sets_to_retrace).difference({last_set})))\n", 263 | "\n", 264 | " while next_to_last_set is not None:\n", 265 | " last_point = next_to_last_point\n", 266 | " last_set = next_to_last_set\n", 267 | " path_key = (sets_to_retrace, last_set, last_point)\n", 268 | " _, next_to_last_set, next_to_last_point = memo[path_key]\n", 269 | "\n", 270 | " optimal_path_in_points_idxs = [last_point] + optimal_path_in_points_idxs\n", 271 | " optimal_path_in_sets_idxs = [last_set] + optimal_path_in_sets_idxs\n", 272 | " sets_to_retrace = tuple(sorted(set(sets_to_retrace).difference({last_set})))\n", 273 | "\n", 274 | " return optimal_path_in_points_idxs, optimal_path_in_sets_idxs, optimal_cost" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 9, 280 | "metadata": { 281 | "collapsed": true 282 | }, 283 | "outputs": [], 284 | "source": [ 285 | "def get_features_for_all_points(all_sets):\n", 286 | " points = []\n", 287 | " for set_ in all_sets:\n", 288 | " color = \"#\" + ''.join(random.choices('0123456789abcdef', k=6))\n", 289 | " points += [\n", 290 | " Feature(geometry=Point(tuple([g.lng, g.lat])),\n", 291 | " properties={\"name\": g.name_,\n", 292 | " \"marker-symbol\": int(g.name_[-1]),\n", 293 | " \"marker-color\": color})\n", 294 | " for g in set_]\n", 295 | " return points" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": 10, 301 | "metadata": { 302 | "collapsed": false 303 | }, 304 | "outputs": [], 305 | "source": [ 306 | "def plot_route_on_map(all_sets, optimal_path_in_points_idxs):\n", 307 | " points = get_features_for_all_points(all_sets)\n", 308 | " \n", 309 | " all_geo_points = get_all_geopoints_from_all_sets(all_sets)\n", 310 | " lng_lat_list = [tuple([all_geo_points[i].lng, all_geo_points[i].lat])\n", 311 | " for i in optimal_path_in_points_idxs]\n", 312 | " route = Feature(geometry=LineString(lng_lat_list),\n", 313 | " properties={\"name\": \"This is our route\",\n", 314 | " \"stroke\": \"black\"})\n", 315 | " \n", 316 | " feature_collection = FeatureCollection(features=points+[route])\n", 317 | " geojsonio.display(json.dumps(feature_collection));" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 11, 323 | "metadata": { 324 | "collapsed": false 325 | }, 326 | "outputs": [ 327 | { 328 | "name": "stdout", 329 | "output_type": "stream", 330 | "text": [ 331 | "[2, 7, 3] [0, 2, 1] 378\n" 332 | ] 333 | } 334 | ], 335 | "source": [ 336 | "optimal_path_in_points, optimal_path_in_sets, optimal_cost = DP_Set_TSP(set_to_points_dict, distances_array)\n", 337 | "print(optimal_path_in_points, optimal_path_in_sets, optimal_cost)" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 12, 343 | "metadata": { 344 | "collapsed": false 345 | }, 346 | "outputs": [], 347 | "source": [ 348 | "# Opens a new tab in the browser :)\n", 349 | "plot_route_on_map(all_sets, optimal_path_in_points)" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "# Part III - Larger Random Experiments" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 13, 362 | "metadata": { 363 | "collapsed": true 364 | }, 365 | "outputs": [], 366 | "source": [ 367 | "def get_random_geo_point(center_lat=32.1, center_lng=34.8, radius=0.1, name_=None):\n", 368 | " geo_point = GeoPoint(lat = center_lat + (radius * random.random()), \n", 369 | " lng = center_lng + (radius * random.random()),\n", 370 | " name_ = name_)\n", 371 | " return geo_point" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 14, 377 | "metadata": { 378 | "collapsed": true 379 | }, 380 | "outputs": [], 381 | "source": [ 382 | "def generate_random_input_in_geo_points(n_sets: int, poisson_lambda: int = 2) -> [{int: int}, {int: int}, np.array]:\n", 383 | " set_to_points_dict = {}\n", 384 | " first_point_idx = 0\n", 385 | " for set_idx in range(n_sets):\n", 386 | " n_points_in_set = 1 + np.random.poisson(poisson_lambda)\n", 387 | " set_to_points_dict[set_idx] = list(range(first_point_idx, first_point_idx + n_points_in_set))\n", 388 | " first_point_idx += n_points_in_set\n", 389 | "\n", 390 | " n_points = first_point_idx\n", 391 | " all_sets = []\n", 392 | " for idx_set in range(n_sets):\n", 393 | " all_sets += [[get_random_geo_point(name_=f's{idx_set}_i{idx_point}') \n", 394 | " for idx_point in range(len(set_to_points_dict[idx_set]))]]\n", 395 | " \n", 396 | " all_geo_points = get_all_geopoints_from_all_sets(all_sets)\n", 397 | " distances_array = np.array([[all_geo_points[i].get_dist_from(all_geo_points[j])\n", 398 | " for i in range(n_points)]\n", 399 | " for j in range(n_points)])\n", 400 | "\n", 401 | " return all_sets, all_geo_points, set_to_points_dict, distances_array" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 15, 407 | "metadata": { 408 | "collapsed": false 409 | }, 410 | "outputs": [ 411 | { 412 | "name": "stdout", 413 | "output_type": "stream", 414 | "text": [ 415 | "[1, 9, 5, 37, 28, 15, 33, 25, 12, 20] [0, 2, 1, 9, 7, 4, 8, 6, 3, 5] 10862\n" 416 | ] 417 | } 418 | ], 419 | "source": [ 420 | "n_sets = 10\n", 421 | "poisson_lambda = 4\n", 422 | "all_sets, all_geo_points, set_to_points_dict, distances_array = generate_random_input_in_geo_points(n_sets, poisson_lambda)\n", 423 | "optimal_path_in_points, optimal_path_in_sets, optimal_cost = DP_Set_TSP(set_to_points_dict, distances_array)\n", 424 | "print(optimal_path_in_points, optimal_path_in_sets, optimal_cost)\n", 425 | "\n", 426 | "# Opens a new tab in the browser :)\n", 427 | "plot_route_on_map(all_sets, optimal_path_in_points)" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "# Extra - Working with non-geo random input" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": 16, 440 | "metadata": { 441 | "collapsed": true 442 | }, 443 | "outputs": [], 444 | "source": [ 445 | "def generate_random_input(n_sets: int, poisson_lambda: int = 2) -> [{int: int}, {int: int}, np.array]:\n", 446 | " set_to_points_dict = {}\n", 447 | " first_point_idx = 0\n", 448 | " for set_idx in range(n_sets):\n", 449 | " n_points_in_set = 1 + np.random.poisson(poisson_lambda)\n", 450 | " set_to_points_dict[set_idx] = list(range(first_point_idx, first_point_idx + n_points_in_set))\n", 451 | " first_point_idx += n_points_in_set\n", 452 | "\n", 453 | " n_points = first_point_idx\n", 454 | " X = np.random.rand(n_points, 3)\n", 455 | " distances_array = np.array([[np.linalg.norm(X[i] - X[j])\n", 456 | " for i in range(n_points)]\n", 457 | " for j in range(n_points)])\n", 458 | "\n", 459 | " return X, set_to_points_dict, distances_array" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": 17, 465 | "metadata": { 466 | "collapsed": true 467 | }, 468 | "outputs": [], 469 | "source": [ 470 | "def scatter_plot(X: np.array, clusters_in_idxs: [[int]]):\n", 471 | " x, y = list(zip(*[[X[c_idx][0], X[c_idx][1]]\n", 472 | " for one_cluster_in_idxs in clusters_in_idxs\n", 473 | " for c_idx in one_cluster_in_idxs]))\n", 474 | " c = [color_idx\n", 475 | " for color_idx, one_cluster_in_idxs in enumerate(clusters_in_idxs)\n", 476 | " for _ in one_cluster_in_idxs]\n", 477 | " df = pd.DataFrame({'x': x, 'y': y, 'c': c})\n", 478 | "\n", 479 | " for color_idx, cluster_in_idxs in enumerate(clusters_in_idxs):\n", 480 | " df_temp = df[df['c'].isin([color_idx])]\n", 481 | " plt.plot(df_temp['x'].tolist(), df_temp['y'].tolist(), 'o', label=color_idx, markersize=8);\n", 482 | "\n", 483 | " plt.legend(loc='upper left', bbox_to_anchor=(1, 1))" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 18, 489 | "metadata": { 490 | "collapsed": true 491 | }, 492 | "outputs": [], 493 | "source": [ 494 | "def plot_route(X, optimal_path_in_points_idxs, set_to_points_dict):\n", 495 | " scatter_plot(X, list(set_to_points_dict.values()))\n", 496 | " for p1, p2 in zip(optimal_path_in_points_idxs[:-1], optimal_path_in_points_idxs[1:]):\n", 497 | " plt.plot([X[p1, 0], X[p2, 0]], [X[p1, 1], X[p2, 1]], color='grey');" 498 | ] 499 | }, 500 | { 501 | "cell_type": "code", 502 | "execution_count": 19, 503 | "metadata": { 504 | "collapsed": false 505 | }, 506 | "outputs": [ 507 | { 508 | "data": { 509 | "image/png": "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\n", 510 | "text/plain": [ 511 | "
" 512 | ] 513 | }, 514 | "metadata": { 515 | "needs_background": "light" 516 | }, 517 | "output_type": "display_data" 518 | } 519 | ], 520 | "source": [ 521 | "n_sets = 5\n", 522 | "poisson_lambda = 3\n", 523 | "X, set_to_points_dict, distances_array = generate_random_input(n_sets, poisson_lambda)\n", 524 | "\n", 525 | "optimal_path_in_points_idxs, optimal_path_in_sets_idxs, optimal_cost = DP_Set_TSP(set_to_points_dict, distances_array)\n", 526 | "plot_route(X, optimal_path_in_points_idxs, set_to_points_dict)" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": null, 532 | "metadata": { 533 | "collapsed": true 534 | }, 535 | "outputs": [], 536 | "source": [] 537 | }, 538 | { 539 | "cell_type": "code", 540 | "execution_count": null, 541 | "metadata": { 542 | "collapsed": true 543 | }, 544 | "outputs": [], 545 | "source": [] 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": null, 550 | "metadata": { 551 | "collapsed": true 552 | }, 553 | "outputs": [], 554 | "source": [] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": null, 559 | "metadata": { 560 | "collapsed": true 561 | }, 562 | "outputs": [], 563 | "source": [] 564 | } 565 | ], 566 | "metadata": { 567 | "anaconda-cloud": {}, 568 | "kernelspec": { 569 | "display_name": "quay_rnd", 570 | "language": "python", 571 | "name": "quay_rnd" 572 | }, 573 | "language_info": { 574 | "codemirror_mode": { 575 | "name": "ipython", 576 | "version": 3 577 | }, 578 | "file_extension": ".py", 579 | "mimetype": "text/x-python", 580 | "name": "python", 581 | "nbconvert_exporter": "python", 582 | "pygments_lexer": "ipython3", 583 | "version": "3.7.3" 584 | } 585 | }, 586 | "nbformat": 4, 587 | "nbformat_minor": 1 588 | } 589 | -------------------------------------------------------------------------------- /PlottingWithPandas/PlottingWithPandas_DatesAndBarPlots.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# PlottingWithPandas\n", 8 | "\n", 9 | "## This notebook was created to serve a [blog post]() by the same name." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "# Python 3.7.3\n", 21 | "import pandas as pd # version 0.23.4\n", 22 | "\n", 23 | "import matplotlib.pyplot as plt # version 3.0.2\n", 24 | "%matplotlib inline " 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "## 1. Setting up the Data:\n", 32 | "\n", 33 | "#### Download and save data from Kaggle [Austin Animal Center Shelter Outcomes](https://www.kaggle.com/aaronschlegel/austin-animal-center-shelter-outcomes-and#aac_shelter_outcomes.csv)" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 2, 39 | "metadata": { 40 | "collapsed": false 41 | }, 42 | "outputs": [ 43 | { 44 | "data": { 45 | "text/html": [ 46 | "
\n", 47 | "\n", 60 | "\n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | "
colordate_of_birthname
0orange2014-07-07NaN
1blue /white2014-06-16Lucy
2white/black2014-03-26*Frida
3black/white2013-03-27Stella Luna
4black/white2013-12-16NaN
\n", 102 | "
" 103 | ], 104 | "text/plain": [ 105 | " color date_of_birth name\n", 106 | "0 orange 2014-07-07 NaN\n", 107 | "1 blue /white 2014-06-16 Lucy\n", 108 | "2 white/black 2014-03-26 *Frida\n", 109 | "3 black/white 2013-03-27 Stella Luna\n", 110 | "4 black/white 2013-12-16 NaN" 111 | ] 112 | }, 113 | "execution_count": 2, 114 | "metadata": {}, 115 | "output_type": "execute_result" 116 | } 117 | ], 118 | "source": [ 119 | "filename = \"aac_shelter_cat_outcome_eng.csv\"\n", 120 | "df = pd.read_csv(filename, \n", 121 | " usecols=['name', 'date_of_birth', 'color'],\n", 122 | " parse_dates=['date_of_birth'])\n", 123 | "df.head()" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "## 2. Our Most Basic Plot" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 3, 136 | "metadata": { 137 | "collapsed": false 138 | }, 139 | "outputs": [ 140 | { 141 | "data": { 142 | "text/html": [ 143 | "
\n", 144 | "\n", 157 | "\n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | "
colordate_of_birthnameweekday_numweekday_name
0orange2014-07-07NaN0Monday
1blue /white2014-06-16Lucy0Monday
2white/black2014-03-26*Frida2Wednesday
3black/white2013-03-27Stella Luna2Wednesday
4black/white2013-12-16NaN0Monday
\n", 211 | "
" 212 | ], 213 | "text/plain": [ 214 | " color date_of_birth name weekday_num weekday_name\n", 215 | "0 orange 2014-07-07 NaN 0 Monday\n", 216 | "1 blue /white 2014-06-16 Lucy 0 Monday\n", 217 | "2 white/black 2014-03-26 *Frida 2 Wednesday\n", 218 | "3 black/white 2013-03-27 Stella Luna 2 Wednesday\n", 219 | "4 black/white 2013-12-16 NaN 0 Monday" 220 | ] 221 | }, 222 | "execution_count": 3, 223 | "metadata": {}, 224 | "output_type": "execute_result" 225 | } 226 | ], 227 | "source": [ 228 | "df['weekday_num'] = pd.DatetimeIndex(df['date_of_birth']).weekday\n", 229 | "df['weekday_name'] = pd.DatetimeIndex(df['date_of_birth']).weekday_name\n", 230 | "df.head()" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 4, 236 | "metadata": { 237 | "collapsed": false 238 | }, 239 | "outputs": [ 240 | { 241 | "data": { 242 | "text/html": [ 243 | "
\n", 244 | "\n", 257 | "\n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | "
weekday_numweekday_namen_pets
00Monday4584
11Tuesday4260
22Wednesday4243
33Thursday4048
44Friday3941
55Saturday3966
66Sunday4379
\n", 311 | "
" 312 | ], 313 | "text/plain": [ 314 | " weekday_num weekday_name n_pets\n", 315 | "0 0 Monday 4584\n", 316 | "1 1 Tuesday 4260\n", 317 | "2 2 Wednesday 4243\n", 318 | "3 3 Thursday 4048\n", 319 | "4 4 Friday 3941\n", 320 | "5 5 Saturday 3966\n", 321 | "6 6 Sunday 4379" 322 | ] 323 | }, 324 | "execution_count": 4, 325 | "metadata": {}, 326 | "output_type": "execute_result" 327 | } 328 | ], 329 | "source": [ 330 | "df_grouped = df.groupby(['weekday_num', 'weekday_name']).size().reset_index(name=\"n_pets\")\n", 331 | "df_grouped" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 5, 337 | "metadata": { 338 | "collapsed": false 339 | }, 340 | "outputs": [ 341 | { 342 | "data": { 343 | "image/png": "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\n", 344 | "text/plain": [ 345 | "
" 346 | ] 347 | }, 348 | "metadata": { 349 | "needs_background": "light" 350 | }, 351 | "output_type": "display_data" 352 | } 353 | ], 354 | "source": [ 355 | "df_grouped.plot.bar(x=\"weekday_name\", y=\"n_pets\", color='blue');" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "## 3. The Birtday Surprise\n", 363 | "\n", 364 | "[Basic statistics](https://en.wikipedia.org/wiki/Birthday_problem) show that in every group of 50 people (or pets) you have a 99% chance of having at least two people (pets) who were born on the same day of the year. \n", 365 | "\n", 366 | "Let's test this!" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 6, 372 | "metadata": { 373 | "collapsed": false 374 | }, 375 | "outputs": [ 376 | { 377 | "data": { 378 | "image/png": "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\n", 379 | "text/plain": [ 380 | "
" 381 | ] 382 | }, 383 | "metadata": { 384 | "needs_background": "light" 385 | }, 386 | "output_type": "display_data" 387 | } 388 | ], 389 | "source": [ 390 | "n_sample = 50\n", 391 | "df_sample = df.sample(n=n_sample)\n", 392 | "df_sample['birthday'] = df_sample['date_of_birth'].dt.strftime('%m-%d')\n", 393 | "df_sample_grouped = df_sample.groupby(['birthday']).size().reset_index(name=\"n_pets\")\n", 394 | "df_sample_grouped.plot.bar(x=\"birthday\", y=\"n_pets\", color='blue');" 395 | ] 396 | }, 397 | { 398 | "cell_type": "markdown", 399 | "metadata": {}, 400 | "source": [ 401 | "## Ho my, the dates look all messed up... Let's try to sort this:" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 7, 407 | "metadata": { 408 | "collapsed": false 409 | }, 410 | "outputs": [ 411 | { 412 | "data": { 413 | "image/png": "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\n", 414 | "text/plain": [ 415 | "
" 416 | ] 417 | }, 418 | "metadata": { 419 | "needs_background": "light" 420 | }, 421 | "output_type": "display_data" 422 | } 423 | ], 424 | "source": [ 425 | "df_sample_grouped = df_sample.groupby(['birthday']).size()\n", 426 | "n_unique_dates = len(df_sample_grouped.index.unique())\n", 427 | "fig = plt.figure(figsize=(n_unique_dates/5, n_unique_dates/10))\n", 428 | "\n", 429 | "ax = df_sample_grouped.plot.bar(x=\"birthday\", y=\"n_pets\", color='blue')\n", 430 | "ax.set_xticklabels(labels=df_sample_grouped.index, \n", 431 | " rotation=70, rotation_mode=\"anchor\", ha=\"right\");\n", 432 | "ax.legend(labels=['n_pets']);\n", 433 | "plt.tight_layout()" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "## 3. Let's plot according to animal color:" 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": 8, 446 | "metadata": { 447 | "collapsed": false 448 | }, 449 | "outputs": [ 450 | { 451 | "data": { 452 | "text/html": [ 453 | "
\n", 454 | "\n", 467 | "\n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | "
colordate_of_birthnameweekday_numweekday_namehas_black
0orange2014-07-07NaN0MondayFalse
1blue /white2014-06-16Lucy0MondayFalse
2white/black2014-03-26*Frida2WednesdayTrue
3black/white2013-03-27Stella Luna2WednesdayTrue
4black/white2013-12-16NaN0MondayTrue
\n", 527 | "
" 528 | ], 529 | "text/plain": [ 530 | " color date_of_birth name weekday_num weekday_name has_black\n", 531 | "0 orange 2014-07-07 NaN 0 Monday False\n", 532 | "1 blue /white 2014-06-16 Lucy 0 Monday False\n", 533 | "2 white/black 2014-03-26 *Frida 2 Wednesday True\n", 534 | "3 black/white 2013-03-27 Stella Luna 2 Wednesday True\n", 535 | "4 black/white 2013-12-16 NaN 0 Monday True" 536 | ] 537 | }, 538 | "execution_count": 8, 539 | "metadata": {}, 540 | "output_type": "execute_result" 541 | } 542 | ], 543 | "source": [ 544 | "df['has_black'] = df['color'].str.contains(\"black\")\n", 545 | "df.head()" 546 | ] 547 | }, 548 | { 549 | "cell_type": "code", 550 | "execution_count": 9, 551 | "metadata": { 552 | "collapsed": false 553 | }, 554 | "outputs": [ 555 | { 556 | "data": { 557 | "text/html": [ 558 | "
\n", 559 | "\n", 572 | "\n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | "
has_blackFalseTrue
weekday_name
Monday28021227
Tuesday25751129
Wednesday26181138
Thursday25271049
Friday24251000
Saturday24171056
Sunday26701162
\n", 623 | "
" 624 | ], 625 | "text/plain": [ 626 | "has_black False True \n", 627 | "weekday_name \n", 628 | "Monday 2802 1227\n", 629 | "Tuesday 2575 1129\n", 630 | "Wednesday 2618 1138\n", 631 | "Thursday 2527 1049\n", 632 | "Friday 2425 1000\n", 633 | "Saturday 2417 1056\n", 634 | "Sunday 2670 1162" 635 | ] 636 | }, 637 | "execution_count": 9, 638 | "metadata": {}, 639 | "output_type": "execute_result" 640 | } 641 | ], 642 | "source": [ 643 | "df_grouped_color = (df.groupby(['weekday_num', 'weekday_name'])['has_black']\n", 644 | " .value_counts()\n", 645 | " .unstack()\n", 646 | " .reset_index(level=0, drop=True))\n", 647 | "df_grouped_color" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": 10, 653 | "metadata": { 654 | "collapsed": false 655 | }, 656 | "outputs": [ 657 | { 658 | "data": { 659 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAY8AAAEsCAYAAAA7Ej+nAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzt3Xm8nPP9///HU4QoKosgTZAgtqChR0L5qK2kqKDE0m8tVUop2n4+SBdaS5dfF9VPKWnta9Io0tLaVcuHSDSWCE2sObFFNlGkEq/fH+/3SSfJ2YYz57omed5vt3PLzHuumbzOnJnrdb13RQRmZmbVWKnoAMzMrP44eZiZWdWcPMzMrGpOHmZmVjUnDzMzq5qTh5mZVc3Jw8zMqubkYWZmVXPyMDOzqq1cdAC1svbaa0f//v2LDsPMrK5MnDjxzYjo3dZxy23y6N+/PxMmTCg6DDOzuiLppfYc52YrMzOrmpOHmZlVzcnDzMyq5uRhZmZVc/IwM7OqOXmYmVnVnDzMzKxqNU8ekrpI+oekP+X7AyQ9ImmqpNGSVsnlq+b70/Lj/SteY2Quf1bS3rWO2czMWtcZNY9TgSkV938CXBARA4E5wLG5/FhgTkRsAlyQj0PSlsBhwCBgGHCxpC4dHaSkmv6YmS1Papo8JPUD9gV+l+8L2B0Ymw+5Cjgg3x6e75Mf3yMfPxy4MSIWRMQLwDRgSC3jNjOz1tW65vFL4HTgg3y/FzA3Ihbm+41A33y7LzAdID8+Lx+/uLyZ5yxB0vGSJkiaMHPmzI78PczMrELNkoek/YA3ImJiZXEzh0Ybj7X2nCULI0ZFRENENPTu3ea6XmZm9iHVcmHEnYD9Je0DdAM+TqqJdJe0cq5d9ANeycc3AusDjZJWBtYCZleUN6l8jpmZFaBmNY+IGBkR/SKiP6nD+96I+CJwH3BwPuwo4NZ8e1y+T3783oiIXH5YHo01ABgIjK9V3GZm1rYilmQ/A7hR0nnAP4DLcvllwDWSppFqHIcBRMRkSWOAp4GFwEkRsajzwzYzsyZKF/fLn4aGhqhmP49aD6ddXt9nM1u+SJoYEQ1tHecZ5mZmVjUnDzMzq9pyuw2tmVk9qNcmc9c8zMysak4eZmZWNScPMzOrmvs8rHD12uZrtiJzzcPMzKrm5GFmZlVzs9Vywk0/ZtaZXPMwM7OqOXmYmVnVnDzMzKxqTh5mZlY1Jw8zM6uak4eZmVWtZslDUjdJ4yU9LmmypB/k8islvSBpUv4ZnMsl6VeSpkl6QtJ2Fa91lKSp+eeolv5PsyJIqumPWRnVcp7HAmD3iHhbUlfg75L+nB/7n4gYu9TxnyPtTz4QGAr8BhgqqSdwNtAABDBR0riImFPD2M3MrBU1q3lE8na+2zX/tDbTbDhwdX7ew0B3SX2AvYG7ImJ2Thh3AcNqFbeZmbWtpn0ekrpImgS8QUoAj+SHzs9NUxdIWjWX9QWmVzy9MZe1VG5mZgWpafKIiEURMRjoBwyRtBUwEtgc2B7oCZyRD2+ucTdaKV+GpOMlTZA0YebMmR85fjMza16njLaKiLnA/cCwiHg1N00tAK4AhuTDGoH1K57WD3illfLm/p9REdEQEQ29e/fu4N/CzMrIAxaKUcvRVr0ldc+3VwP2BJ7J/Rgo/VUOAJ7KTxkHHJlHXe0AzIuIV4E7gL0k9ZDUA9grl5mZWUFqOdqqD3CVpC6kJDUmIv4k6V5JvUnNUZOAE/LxtwP7ANOAd4BjACJitqRzgUfzcedExOwaxm22QvGKzPZhaHn9wzY0NMSECRPafXy9f4HqOf56jh0cf1scf+vKFr+kiRHR0NZxnmFuZmZVc/IwM7OqOXmYmVnVnDzMzKxqTh5mZlY1Jw8zM6uak4eZmVXNycPMzKrm5GFmZlVz8jAzs6o5eZiZWdWcPMzMrGpOHmZmVjUnDzMzq5qTh5mZVc3Jw8zMqubkYWZmVatp8pDUTdJ4SY9LmizpB7l8gKRHJE2VNFrSKrl81Xx/Wn68f8Vrjczlz0rau5Zxm5lZ62pd81gA7B4RnwQGA8Mk7QD8BLggIgYCc4Bj8/HHAnMiYhPggnwckrYEDgMGAcOAi/Pe6GZmVoCaJo9I3s53u+afAHYHxubyq4AD8u3h+T758T2UNvgdDtwYEQsi4gVgGjCklrGbmVnLat7nIamLpEnAG8BdwHPA3IhYmA9pBPrm232B6QD58XlAr8ryZp5T+X8dL2mCpAkzZ86sxa9jZmZ0QvKIiEURMRjoR6otbNHcYflftfBYS+VL/1+jIqIhIhp69+79YUM2M7M2dNpoq4iYC9wP7AB0l7Ryfqgf8Eq+3QisD5AfXwuYXVnezHPMzKyT1Xq0VW9J3fPt1YA9gSnAfcDB+bCjgFvz7XH5PvnxeyMicvlheTTWAGAgML6WsZuZWctWbvuQj6QPcFUeGbUSMCYi/iTpaeBGSecB/wAuy8dfBlwjaRqpxnEYQERMljQGeBpYCJwUEYtqHLuZmbVA6cJ++dPQ0BATJkxo9/FpUFft1Pp9ruf46zl2cPxtcfytK1v8kiZGRENbx3mGuZmZVc3Jw8zMqubkYWZmVXPyMDOzqtV6tFXduG7LLYsOwcysbrjmYWZmVXPyMDOzqjl5mJlZ1Zw8zMysak4eZmZWNScPMzOrmofqmpkVqF6nCbjmYWZmVXPyMDOzqrnZygpXr9V2sxWZax5mZlY11zyWE756N7POVLOah6T1Jd0naYqkyZJOzeXflzRD0qT8s0/Fc0ZKmibpWUl7V5QPy2XTJJ1Zq5jNzKx9alnzWAh8KyIek7QmMFHSXfmxCyLiZ5UHS9qStGf5IOATwN2SNs0PXwR8FmgEHpU0LiKermHsZmbWipolj4h4FXg1354vaQrQt5WnDAdujIgFwAuSpgFD8mPTIuJ5AEk35mOdPKwU3GRoK6JO6TCX1B/YFngkF50s6QlJl0vqkcv6AtMrntaYy1oqb+7/OV7SBEkTZs6c2YG/gZmZVap58pC0BnATcFpEvAX8BtgYGEyqmfy86dBmnh6tlC9bGDEqIhoioqF3794fOXYzM2teTUdbSepKShzXRcQfACLi9YrHfwv8Kd9tBNaveHo/4JV8u6VyMzMrQC1HWwm4DJgSEb+oKO9TcdiBwFP59jjgMEmrShoADATGA48CAyUNkLQKqVN9XK3iNjOzttWy5rET8CXgSUmTctm3gcMlDSY1Pb0IfBUgIiZLGkPqCF8InBQRiwAknQzcAXQBLo+IyTWM22yFUu8d/vUef72q5Wirv9N8f8XtrTznfOD8Zspvb+15ZmbWudpstpK0uqSV8u1NJe2f+zLMzGwF1Z4+jweAbpL6AvcAxwBX1jIoMzMrt/YkD0XEO8BBwP9GxIGAGxnNzFZg7UoeknYEvgjclsu8oKKZ2QqsPcnjVGAkcHMeEbURcF9twzIzszJrTw1i3YjYv+lORDwv6W81jMnMzEquPTWPke0sMzOzFUSLNQ9JnwP2AfpK+lXFQx8nTeIzM7MVVGvNVq8AE4D9gYkV5fOBb9QyKDMzK7cWk0dEPA48Lun6fNwGEfFsp0VmZmal1Z4+j2HAJOAvAJIGS/LChGZmK7D2JI/vk3b0mwsQEZOA/rULyczMyq49yWNhRMyreSRmZlY32jPP4ylJRwBdJA0ETgEeqm1YZmZWZu2peXwdGAQsAG4A3gJOq2VQZmZWbm3WPPKiiN+R9JN0N+bXPiwzMyuz9uznsb2kJ4EnSLsCPi7pU+143vqS7pM0RdJkSafm8p6S7pI0Nf/bI5dL0q8kTZP0hKTtKl7rqHz8VElHffhf18zMOkJ7mq0uA74WEf0joj9wEnBFO563EPhWRGwB7ACcJGlL4EzgnogYSNof5Mx8/OdI+5YPBI4HfgMp2QBnA0NJo77Obko4ZmZWjPYkj/kRsXghxLy9bJtNVxHxakQ8lm/PB6YAfYHhwFX5sKuAA/Lt4cDVkTwMdJfUB9gbuCsiZkfEHOAu0twTMzMrSHtGW42XdCmpszyAQ4H7m5qVmhJEayT1B7YFHiGt0vtqfu6rktbJh/UFplc8rTGXtVRuZmYFaU/yGJz/PXup8k+TksnurT1Z0hrATcBpEfGWpBYPbaYsWilv7v86ntTkxQYbbNBaWGZm9hG0Z7TVbq09LumoiLiqhce6khLHdRHxh1z8uqQ+udbRB3gjlzcC61c8vR9pccZGYNelyu9vIdZRwCiAhoaGZhOMmZl9dO3p82jLqc0VKlUxLgOmRMQvKh4aBzSNmDoKuLWi/Mg86moHYF5u3roD2EtSj9xRvlcuMzOzgnTEXuQttUPtBHyJNLx3Ui77NvBjYIykY4GXgUPyY7eT9g+ZBrwDHAMQEbMlnQs8mo87JyJmd0DcZmb2IXVE8mi2eSiPymopsezRzPFBGgbc3GtdDlz+YQM0M7OO1RHNVi32gJuZ2fKpzZqHpFWBL5CWYV98fESck28+WJPIzMystNrTbHUrMI+0Fe2CpR+MiJM7OigzMyu39iSPfhHhGd1mZrZYe/o8HpK0dc0jMTOzutGemsfOwNGSXiA1W4k0OGqbmkZmZmal1Z7k8bmaR2FmZnWlPcuTvNQZgZiZWf3oiHkeZma2gnHyMDOzqjl5mJlZ1Zw8zMysak4eZmZWNScPMzOrmpOHmZlVzcnDzMyq5uRhZmZVq2nykHS5pDckPVVR9n1JMyRNyj/7VDw2UtI0Sc9K2ruifFgumybpzFrGbGZmbat1zeNKoLnl3C+IiMH553YASVsChwGD8nMultRFUhfgItIaW1sCh+djzcysIB2xh3mLIuIBSf3befhw4MaIWAC8IGkaMCQ/Ni0ingeQdGM+9ukODtfMzNqpqD6PkyU9kZu1euSyvsD0imMac1lL5cuQdLykCZImzJw5sxZxm5kZxSSP3wAbA4OBV4Gf53I1c2y0Ur5sYcSoiGiIiIbevXt3RKxmZtaMmjZbNSciXm+6Lem3wJ/y3UZg/YpD+wGv5NstlZuZWQE6veYhqU/F3QOBppFY44DDJK0qaQAwEBgPPAoMlDRA0iqkTvVxnRmzmZktqaY1D0k3ALsCa0tqBM4GdpU0mNT09CLwVYCImCxpDKkjfCFwUkQsyq9zMnAH0AW4PCIm1zJuMzNrXa1HWx3eTPFlrRx/PnB+M+W3A7d3YGhmZvYReIa5mZlVzcnDzMyq5uRhZmZVc/IwM7OqOXmYmVnVnDzMzKxqTh5mZlY1Jw8zM6uak4eZmVXNycPMzKrm5GFmZlVz8jAzs6o5eZiZWdWcPMzMrGpOHmZmVjUnDzMzq5qTh5mZVa2myUPS5ZLekPRURVlPSXdJmpr/7ZHLJelXkqZJekLSdhXPOSofP1XSUbWM2czM2lbTbWiBK4FfA1dXlJ0J3BMRP5Z0Zr5/BvA5YGD+GQr8BhgqqSdp7/MG0r7nEyWNi4g5NY7dlmPvv/8+jY2NvPfeex/5tTb91a86IKKWTZkypUNep1u3bvTr14+uXbt2yOvZiq3We5g/IKn/UsXDgV3z7auA+0nJYzhwdUQE8LCk7pL65GPviojZAJLuAoYBN9Qydlu+NTY2suaaa9K/f38kfaTXmrVoUQdF1bxeW2zxkV8jIpg1axaNjY0MGDCgA6KyFV0RfR7rRsSrAPnfdXJ5X2B6xXGNuayl8mVIOl7SBEkTZs6c2eGB2/Ljvffeo1evXh85cdQLSfTq1atDalpmUK4O8+a+xdFK+bKFEaMioiEiGnr37t2hwdnyZ0VJHE1WtN/XaquI5PF6bo4i//tGLm8E1q84rh/wSivlZmZWkCKSxzigacTUUcCtFeVH5lFXOwDzcrPWHcBeknrkkVl75TKzmnnxxRfZaqutOvx1b7jlFs44//xmH9twyJAP9ZpHH300Y8eO/ShhmVWtph3mkm4gdXivLamRNGrqx8AYSccCLwOH5MNvB/YBpgHvAMcARMRsSecCj+bjzmnqPDczs2LUerTV4S08tEczxwZwUguvczlweQeGZtamRYsWcdxxx/HQQw/Rt29fbr31Vq699lpGjRrFv//9bzbZZBOuueYaAG694w5+eskldFlpJdZcYw3+dNVVLb7ujNdeY8QJJ/DSjBl8YZ99OP3EE5d4/O133uFLp5zC3LfeYuH77/Ojn/2M4cOHA3D11Vfzs5/9DElss802i///Jt/73veYPn06l19+OSutVKYuTVve1Hqeh1ndmjp1KjfccAO//e1vGTFiBDfddBMHHXQQxx13HADf/e53ueyyyzhit9342SWX8PtLLqHPuusy7623Wn3dx556ir/ffDOrdevGZw8/nM/usgvbDhq0+PFuq6zC1b/8JWuusQaz5sxhn2OOYf/99+fpp5/m/PPP58EHH2Tttddm9uwlK+Cnn3468+bN44orrnDnuNWcL03MWjBgwAAGDx4MwKc+9SlefPFFnnrqKf7rv/6Lrbfemuuuu47JkycDMGTbbTn5u9/l6rFjWfTBB62+7q477kjP7t1ZrVs39t1jDx557LElHo8IzrvwQnY56CC+cNxxzJgxg9dff517772Xgw8+mLXXXhuAnj17Ln7Oueeey9y5c7n00kudOKxTOHmYtWDVVVddfLtLly4sXLiQo48+ml//+tc8+eSTnH322YvnTfz8rLP49te/zozXXmPXgw9m9ty5Lb7u0if3pe+Pve023pwzh3tGj+b+sWNZd911ee+994iIFhPD9ttvz8SJE5epjZjVipOHWRXmz59Pnz59eP/997nuuusWl78wfTqf2mYbRp58Mr169GDGa6+1+Br3/9//MWfePN597z3+fO+9DNl22yUef+vtt+ndsyddu3blb+PH89JLLwGwxx57MGbMGGbNmgWwRKIYNmwYZ555Jvvuuy/z58/vyF/ZrFnu8zCrwrnnnsvQoUPZcMMN2XrrrRefqL//85/z/EsvEcAuQ4ey1WabtfgaO2y7LSeOHMkL06fzhX32WaK/A+DgfffliyefzB6HHspWm2/O5ptvDsCgQYP4zne+w2c+8xm6dOnCtttuy5VXXrn4eYcccgjz589n//335/bbb2e11Vbr8N/frInSIKflT0NDQ0yYMKHdx1+/1Be4ox2R28ZrpZ7jLyL2KVOmsEUHrBkFMOupp9o+6CPo1YHzTZr7vev5swOOvy3Vxi9pYkQ0tHWcm63MzKxq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\n", 660 | "text/plain": [ 661 | "
" 662 | ] 663 | }, 664 | "metadata": { 665 | "needs_background": "light" 666 | }, 667 | "output_type": "display_data" 668 | } 669 | ], 670 | "source": [ 671 | "ax = df_grouped_color.plot.bar(stacked=True, color=['brown', 'black']);\n", 672 | "ax.set_xticklabels(labels=df_grouped_color.index, \n", 673 | " rotation=70, rotation_mode=\"anchor\", ha=\"right\");\n", 674 | "ax.set_xlabel('');\n", 675 | "ax.set_ylabel('n_pets');" 676 | ] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "execution_count": null, 681 | "metadata": { 682 | "collapsed": true 683 | }, 684 | "outputs": [], 685 | "source": [] 686 | }, 687 | { 688 | "cell_type": "code", 689 | "execution_count": null, 690 | "metadata": { 691 | "collapsed": true 692 | }, 693 | "outputs": [], 694 | "source": [] 695 | } 696 | ], 697 | "metadata": { 698 | "anaconda-cloud": {}, 699 | "kernelspec": { 700 | "display_name": "quay_rnd", 701 | "language": "python", 702 | "name": "quay_rnd" 703 | }, 704 | "language_info": { 705 | "codemirror_mode": { 706 | "name": "ipython", 707 | "version": 3 708 | }, 709 | "file_extension": ".py", 710 | "mimetype": "text/x-python", 711 | "name": "python", 712 | "nbconvert_exporter": "python", 713 | "pygments_lexer": "ipython3", 714 | "version": "3.7.3" 715 | } 716 | }, 717 | "nbformat": 4, 718 | "nbformat_minor": 1 719 | } 720 | --------------------------------------------------------------------------------