├── .gitignore ├── README.md ├── Week2.ipynb ├── Week3.ipynb ├── Week4.ipynb ├── Week5.ipynb ├── Week5_C1M5L2_Methods_and_Classes_V3.ipynb ├── Week5_C1M5L3_Code_Reuse_V2.ipynb ├── Week5_C1M5_Object_Oriented_Programming_V7.ipynb ├── Week6_C1M6L1_Putting_It_All_Together.ipynb └── Week6_C1M6L2_Final_Project_V3.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | */.ipynb_checkpoints/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Coursera: Crash Course on Python 2 | ---------------------------------- 3 | 4 | A quick revision of Python during COVID-19 :( 5 | 6 | This repository contains all the solution codes as notebook of the course assessments done by me. 7 | 8 | N.B. All the codes were tested and passed on coursera. If you find any error or face any issue, let me know through issue section. There is a chance that they may change or add problems in future. If you don't find any problem solution of practice quiz or module assessment, open an issue including the problem description along week number. 9 | 10 | 11 | **Crash Course on Python** part of the ***Google IT Automation with Python Professional Certificate*** 12 | 13 | 14 | - **Week 2: Basic Python Syntax** 15 | - [x] Practice Quiz: Expressions and Variables 16 | - [x] Practice Quiz: Functions 17 | - [x] Practice Quiz: Conditionals 18 | - [x] Module 2 Graded Assessment 19 | 20 | - **Week 3: Loops** 21 | - [x] Practice Quiz: While Loops 22 | - [x] Practice Quiz: For Loops 23 | - [x] Practice Quiz: Recursion(Optional) 24 | - [x] Module 3 Graded Assessment 25 | 26 | - **Week 4: Strings, Lists and Dictionaries** 27 | - [x] Practice Quiz: Strings 28 | - [x] Practice Quiz: Lists 29 | - [x] Practice Quiz: Dictionaries 30 | - [x] Module 4 Graded Assessment 31 | 32 | - **Week 5: Object Oriented Programming (Optional)** 33 | - [x] Basics of Object Oriented Programming 34 | - [x] Methods and Classes (Optional) 35 | - [x] Code Reuse (Optional) 36 | - [x] Practice Notebook: Object Oriented Programming (Optional) 37 | 38 | - **Week 6: Final Project** 39 | - [x] Practice Notebook - Putting It All Together 40 | - [x] Final Project: WordCloud -------------------------------------------------------------------------------- /Week2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Practice Quiz: Expressions and Variables" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "**Problem:** In this scenario, two friends are eating dinner at a restaurant. The bill comes in the amount of 47.28 dollars. The friends decide to split the bill evenly between them, after adding 15% tip for the service. Calculate the tip, the total amount to pay, and each friend's share, then output a message saying \"Each person needs to pay: \" followed by the resulting number." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "Each person needs to pay:27.186\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "bill = 47.28\n", 32 | "tip = bill * 0.15\n", 33 | "total = bill + tip\n", 34 | "share = total/2.0\n", 35 | "print(\"Each person needs to pay:{}\".format(share))" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "**Problem:** This code is supposed to take two numbers, divide one by another so that the result is equal to 1, and display the result on the screen. Unfortunately, there is an error in the code. Find the error and fix it, so that the output is correct." 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "1.0\n" 55 | ] 56 | } 57 | ], 58 | "source": [ 59 | "numerator = 10\n", 60 | "denominator = 10\n", 61 | "result = numerator / denominator\n", 62 | "print(result)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "**Problem:** Combine the variables to display the sentence \"How do you like Python so far?\"" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "How do you like Python so far?\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "word1 = \"How\"\n", 87 | "word2 = \"do\"\n", 88 | "word3 = \"you\"\n", 89 | "word4 = \"like\"\n", 90 | "word5 = \"Python\"\n", 91 | "word6 = \"so\"\n", 92 | "word7 = \"far?\"\n", 93 | "\n", 94 | "print(word1 +' '+ word2 + ' ' + word3 + ' ' + word4 + ' ' + word5 + ' ' + word6 + ' ' + word7 )" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "**Problem:** This code is supposed to display \"2 + 2 = 4\" on the screen, but there is an error. Find the error in the code and fix it, so that the output is correct." 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 4, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "2 + 2 = 4\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "print(\"2 + 2 = \" + str(2 + 2))" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "## Practice Quiz: Functions" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "**Problem:** This function converts miles to kilometers (km).\n", 133 | "\n", 134 | " 1. Complete the function to return the result of the conversion\n", 135 | " 2. Call the function to convert the trip distance from miles to kilometers\n", 136 | " 3. Fill in the blank to print the result of the conversion\n", 137 | " 4. Calculate the round-trip in kilometers by doubling the result, and fill in the blank to print the result" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 5, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "name": "stdout", 147 | "output_type": "stream", 148 | "text": [ 149 | "The distance in kilometers is 88.0\n", 150 | "The round-trip in kilometers is 176.0\n" 151 | ] 152 | } 153 | ], 154 | "source": [ 155 | "# 1) Complete the function to return the result of the conversion\n", 156 | "def convert_distance(miles):\n", 157 | "\tkm = miles * 1.6 # approximately 1.6 km in 1 mile\n", 158 | "\treturn km\n", 159 | "\n", 160 | "my_trip_miles = 55\n", 161 | "\n", 162 | "# 2) Convert my_trip_miles to kilometers by calling the function above\n", 163 | "my_trip_km = convert_distance(my_trip_miles)\n", 164 | "\n", 165 | "# 3) Fill in the blank to print the result of the conversion\n", 166 | "print(\"The distance in kilometers is \" + str(my_trip_km))\n", 167 | "\n", 168 | "# 4) Calculate the round-trip in kilometers by doubling the result,\n", 169 | "# and fill in the blank to print the result\n", 170 | "print(\"The round-trip in kilometers is \" + str(2.0*my_trip_km))" 171 | ] 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "metadata": {}, 176 | "source": [ 177 | "**Problem:** This function compares two numbers and returns them in increasing order.\n", 178 | "\n", 179 | " 1. Fill in the blanks, so the print statement displays the result of the function call in order.\n", 180 | "\n", 181 | "Hint: if a function returns multiple values, don't forget to store these values in multiple variables" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 6, 187 | "metadata": {}, 188 | "outputs": [ 189 | { 190 | "name": "stdout", 191 | "output_type": "stream", 192 | "text": [ 193 | "99 100\n" 194 | ] 195 | } 196 | ], 197 | "source": [ 198 | "# This function compares two numbers and returns them\n", 199 | "# in increasing order.\n", 200 | "def order_numbers(number1, number2):\n", 201 | "\tif number2 > number1:\n", 202 | "\t\treturn number1, number2\n", 203 | "\telse:\n", 204 | "\t\treturn number2, number1\n", 205 | "\n", 206 | "# 1) Fill in the blanks so the print statement displays the result\n", 207 | "# of the function call\n", 208 | "smaller, bigger = order_numbers(100, 99)\n", 209 | "print(smaller, bigger)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "**Problem:** Let's revisit our lucky_number function. We want to change it, so that instead of printing the message, it returns the message. This way, the calling line can print the message, or do something else with it if needed. Fill in the blanks to complete the code to make it work." 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 7, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "Hello Kay. Your lucky number is 27\n", 229 | "Hello Cameron. Your lucky number is 63\n" 230 | ] 231 | } 232 | ], 233 | "source": [ 234 | "def lucky_number(name):\n", 235 | " number = len(name) * 9\n", 236 | " message = \"Hello \" + name + \". Your lucky number is \" + str(number)\n", 237 | " return message \n", 238 | "print(lucky_number(\"Kay\"))\n", 239 | "print(lucky_number(\"Cameron\"))" 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "metadata": {}, 245 | "source": [ 246 | "## Practice Quiz: Conditionals" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "**Problem:** Complete the script by filling in the missing parts. The function receives a name, then returns a greeting based on whether or not that name is \"Taylor\"." 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 8, 259 | "metadata": {}, 260 | "outputs": [ 261 | { 262 | "name": "stdout", 263 | "output_type": "stream", 264 | "text": [ 265 | "Welcome back Taylor!\n", 266 | "Hello there, John\n" 267 | ] 268 | } 269 | ], 270 | "source": [ 271 | "def greeting(name):\n", 272 | " if name == \"Taylor\":\n", 273 | " return \"Welcome back Taylor!\"\n", 274 | " else:\n", 275 | " return \"Hello there, \" + name\n", 276 | "\n", 277 | "print(greeting(\"Taylor\"))\n", 278 | "print(greeting(\"John\"))" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "**Problem:** If a filesystem has a block size of 4096 bytes, this means that a file comprised of only one byte will still use 4096 bytes of storage. A file made up of 4097 bytes will use 4096*2=8192 bytes of storage. Knowing this, can you fill in the gaps in the calculate_storage function below, which calculates the total number of bytes needed to store a file of a given size?" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 9, 291 | "metadata": {}, 292 | "outputs": [ 293 | { 294 | "name": "stdout", 295 | "output_type": "stream", 296 | "text": [ 297 | "4096\n", 298 | "4096\n", 299 | "8192\n", 300 | "8192\n" 301 | ] 302 | } 303 | ], 304 | "source": [ 305 | "def calculate_storage(filesize):\n", 306 | " block_size = 4096\n", 307 | " # Use floor division to calculate how many blocks are fully occupied\n", 308 | " full_blocks = int(filesize/block_size)\n", 309 | " # Use the modulo operator to check whether there's any remainder\n", 310 | " partial_block_remainder = filesize%block_size\n", 311 | " # Depending on whether there's a remainder or not, return\n", 312 | " # the total number of bytes required to allocate enough blocks\n", 313 | " # to store your data.\n", 314 | " if partial_block_remainder > 0:\n", 315 | " return (full_blocks+1)*block_size\n", 316 | " return full_blocks*block_size\n", 317 | "\n", 318 | "print(calculate_storage(1)) # Should be 4096\n", 319 | "print(calculate_storage(4096)) # Should be 4096\n", 320 | "print(calculate_storage(4097)) # Should be 8192\n", 321 | "print(calculate_storage(6000)) # Should be 8192" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "## Module 2 Graded Assessment\n" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "**Problem:** Complete the function by filling in the missing parts. The color_translator function receives the name of a color, then prints its hexadecimal value. Currently, it only supports the three additive primary colors (red, green, blue), so it returns \"unknown\" for all other colors." 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": 10, 341 | "metadata": {}, 342 | "outputs": [ 343 | { 344 | "name": "stdout", 345 | "output_type": "stream", 346 | "text": [ 347 | "#0000ff\n", 348 | "unknown\n", 349 | "#ff0000\n", 350 | "unknown\n", 351 | "#00ff00\n", 352 | "unknown\n" 353 | ] 354 | } 355 | ], 356 | "source": [ 357 | "def color_translator(color):\n", 358 | "\tif color == \"red\":\n", 359 | "\t\thex_color = \"#ff0000\"\n", 360 | "\telif color == \"green\":\n", 361 | "\t\thex_color = \"#00ff00\"\n", 362 | "\telif color == \"blue\":\n", 363 | "\t\thex_color = \"#0000ff\"\n", 364 | "\telse:\n", 365 | "\t\thex_color = \"unknown\"\n", 366 | "\treturn hex_color\n", 367 | "\n", 368 | "print(color_translator(\"blue\")) # Should be #0000ff\n", 369 | "print(color_translator(\"yellow\")) # Should be unknown\n", 370 | "print(color_translator(\"red\")) # Should be #ff0000\n", 371 | "print(color_translator(\"black\")) # Should be unknown\n", 372 | "print(color_translator(\"green\")) # Should be #00ff00\n", 373 | "print(color_translator(\"\")) # Should be unknown" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": {}, 379 | "source": [ 380 | "**Problem:** Students in a class receive their grades as Pass/Fail. Scores of 60 or more (out of 100) mean that the grade is \"Pass\". For lower scores, the grade is \"Fail\". In addition, scores above 95 (not included) are graded as \"Top Score\". Fill in this function so that it returns the proper grade." 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": 11, 386 | "metadata": {}, 387 | "outputs": [ 388 | { 389 | "name": "stdout", 390 | "output_type": "stream", 391 | "text": [ 392 | "Pass\n", 393 | "Fail\n", 394 | "Pass\n", 395 | "Pass\n", 396 | "Top Score\n", 397 | "Fail\n" 398 | ] 399 | } 400 | ], 401 | "source": [ 402 | "def exam_grade(score):\n", 403 | "\tif score>95:\n", 404 | "\t\tgrade = \"Top Score\"\n", 405 | "\telif score >= 60:\n", 406 | "\t\tgrade = \"Pass\"\n", 407 | "\telse:\n", 408 | "\t\tgrade = \"Fail\"\n", 409 | "\treturn grade\n", 410 | "\n", 411 | "print(exam_grade(65)) # Should be Pass\n", 412 | "print(exam_grade(55)) # Should be Fail\n", 413 | "print(exam_grade(60)) # Should be Pass\n", 414 | "print(exam_grade(95)) # Should be Pass\n", 415 | "print(exam_grade(100)) # Should be Top Score\n", 416 | "print(exam_grade(0)) # Should be Fail" 417 | ] 418 | }, 419 | { 420 | "cell_type": "markdown", 421 | "metadata": {}, 422 | "source": [ 423 | "**Problem:** Complete the body of the format_name function. This function receives the first_name and last_name parameters and then returns a properly formatted string.\n", 424 | "\n", 425 | "Specifically:\n", 426 | "\n", 427 | "If both the last_name and the first_name parameters are supplied, the function should return like so:" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "```python\n", 435 | "print(format_name(\"Ella\", \"Fitzgerald\"))\n", 436 | "Output:\n", 437 | "Name: Fitzgerald, Ella\n", 438 | "```" 439 | ] 440 | }, 441 | { 442 | "cell_type": "markdown", 443 | "metadata": {}, 444 | "source": [ 445 | "If only one name parameter is supplied (either the first name or the last name) , the function should return like so:" 446 | ] 447 | }, 448 | { 449 | "cell_type": "markdown", 450 | "metadata": {}, 451 | "source": [ 452 | "```python\n", 453 | "print(format_name(\"Adele\", \"\"))\n", 454 | "Output:\n", 455 | "Name: Adele\n", 456 | "```\n", 457 | "\n", 458 | "or\n", 459 | "\n", 460 | "```python\n", 461 | "print(format_name(\"\", \"Einstein\"))\n", 462 | "Output:\n", 463 | "Name: Einstein\n", 464 | "```" 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": {}, 470 | "source": [ 471 | "Finally, if both names are blank, the function should return the empty string:" 472 | ] 473 | }, 474 | { 475 | "cell_type": "markdown", 476 | "metadata": {}, 477 | "source": [ 478 | "```python\n", 479 | "print(format_name(\"\", \"\"))\n", 480 | "Output:\n", 481 | "\"\"\n", 482 | "```" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 12, 488 | "metadata": {}, 489 | "outputs": [ 490 | { 491 | "name": "stdout", 492 | "output_type": "stream", 493 | "text": [ 494 | "Name: Hemingway, Ernest\n", 495 | "Name: Madonna\n", 496 | "Name: Voltaire\n", 497 | "\n" 498 | ] 499 | } 500 | ], 501 | "source": [ 502 | "def format_name(first_name, last_name):\n", 503 | "\t# code goes here\n", 504 | "\tstring = \"Name: \"\n", 505 | "\tif first_name != \"\" and last_name != \"\":\n", 506 | "\t string += last_name + \", \" + first_name\n", 507 | "\telif first_name != \"\" and last_name == \"\":\n", 508 | "\t string += first_name\n", 509 | "\telif first_name == \"\" and last_name != \"\":\n", 510 | "\t string += last_name\n", 511 | "\telse:\n", 512 | "\t string = \"\"\n", 513 | "\treturn string \n", 514 | "\n", 515 | "print(format_name(\"Ernest\", \"Hemingway\"))\n", 516 | "# Should be \"Name: Hemingway, Ernest\"\n", 517 | "\n", 518 | "print(format_name(\"\", \"Madonna\"))\n", 519 | "# Should be \"Name: Madonna\"\n", 520 | "\n", 521 | "print(format_name(\"Voltaire\", \"\"))\n", 522 | "# Should be \"Name: Voltaire\"\n", 523 | "\n", 524 | "print(format_name(\"\", \"\"))\n", 525 | "# Should be \"\"" 526 | ] 527 | }, 528 | { 529 | "cell_type": "markdown", 530 | "metadata": {}, 531 | "source": [ 532 | "**Problem:** The longest_word function is used to compare 3 words. It should return the word with the most number of characters (and the first in the list when they have the same length). Fill in the blank to make this happen." 533 | ] 534 | }, 535 | { 536 | "cell_type": "code", 537 | "execution_count": 13, 538 | "metadata": {}, 539 | "outputs": [ 540 | { 541 | "name": "stdout", 542 | "output_type": "stream", 543 | "text": [ 544 | "chair\n", 545 | "beyond\n", 546 | "notebook\n" 547 | ] 548 | } 549 | ], 550 | "source": [ 551 | "def longest_word(word1, word2, word3):\n", 552 | "\tif len(word1) >= len(word2) and len(word1) >= len(word3):\n", 553 | "\t\tword = word1\n", 554 | "\telif len(word2) >= len(word1) and len(word2) >= len(word3):\n", 555 | "\t\tword = word2\n", 556 | "\telse:\n", 557 | "\t\tword = word3\n", 558 | "\treturn(word)\n", 559 | "\n", 560 | "print(longest_word(\"chair\", \"couch\", \"table\"))\n", 561 | "print(longest_word(\"bed\", \"bath\", \"beyond\"))\n", 562 | "print(longest_word(\"laptop\", \"notebook\", \"desktop\"))" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": {}, 568 | "source": [ 569 | "**Problem:** The fractional_part function divides the numerator by the denominator, and returns just the fractional part (a number between 0 and 1). Complete the body of the function so that it returns the right number. Note: Since division by 0 produces an error, if the denominator is 0, the function should return 0 instead of attempting the division." 570 | ] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": 14, 575 | "metadata": {}, 576 | "outputs": [ 577 | { 578 | "name": "stdout", 579 | "output_type": "stream", 580 | "text": [ 581 | "0.0\n", 582 | "0.25\n", 583 | "0.6666666666666667\n", 584 | "0.5\n", 585 | "0\n", 586 | "0.0\n" 587 | ] 588 | } 589 | ], 590 | "source": [ 591 | "def fractional_part(numerator, denominator):\n", 592 | "\t# Operate with numerator and denominator to \n", 593 | "\tif denominator:\n", 594 | "\t res = numerator/denominator\n", 595 | "\telse:\n", 596 | "\t res = 0\n", 597 | "\tres = res - int(res)\n", 598 | "# keep just the fractional part of the quotient\n", 599 | "\treturn res\n", 600 | "\n", 601 | "print(fractional_part(5, 5)) # Should be 0\n", 602 | "print(fractional_part(5, 4)) # Should be 0.25\n", 603 | "print(fractional_part(5, 3)) # Should be 0.66...\n", 604 | "print(fractional_part(5, 2)) # Should be 0.5\n", 605 | "print(fractional_part(5, 0)) # Should be 0\n", 606 | "print(fractional_part(0, 5)) # Should be 0" 607 | ] 608 | } 609 | ], 610 | "metadata": { 611 | "kernelspec": { 612 | "display_name": "Python 3", 613 | "language": "python", 614 | "name": "python3" 615 | }, 616 | "language_info": { 617 | "codemirror_mode": { 618 | "name": "ipython", 619 | "version": 3 620 | }, 621 | "file_extension": ".py", 622 | "mimetype": "text/x-python", 623 | "name": "python", 624 | "nbconvert_exporter": "python", 625 | "pygments_lexer": "ipython3", 626 | "version": "3.7.4" 627 | } 628 | }, 629 | "nbformat": 4, 630 | "nbformat_minor": 4 631 | } 632 | -------------------------------------------------------------------------------- /Week3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Practice Quiz: While Loops" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "**Problem:** Fill in the blanks to make the print_prime_factors function print all the prime factors of a number. A prime factor is a number that is prime and divides another without a remainder." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "2\n", 27 | "2\n", 28 | "5\n", 29 | "5\n" 30 | ] 31 | }, 32 | { 33 | "data": { 34 | "text/plain": [ 35 | "'Done'" 36 | ] 37 | }, 38 | "execution_count": 1, 39 | "metadata": {}, 40 | "output_type": "execute_result" 41 | } 42 | ], 43 | "source": [ 44 | "def print_prime_factors(number):\n", 45 | " # Start with two, which is the first prime\n", 46 | " factor = 2\n", 47 | " # Keep going until the factor is larger than the number\n", 48 | " while factor <= number:\n", 49 | " # Check if factor is a divisor of number\n", 50 | " if number % factor == 0:\n", 51 | " # If it is, print it and divide the original number\n", 52 | " print(factor)\n", 53 | " number = number / factor\n", 54 | " else:\n", 55 | " # If it's not, increment the factor by one\n", 56 | " factor += 1\n", 57 | " return \"Done\"\n", 58 | "\n", 59 | "print_prime_factors(100)\n", 60 | "# Should print 2,2,5,5\n", 61 | "# DO NOT DELETE THIS COMMENT" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "**Problem:** The following code can lead to an infinite loop. Fix the code so that it can finish successfully for all numbers.\n", 69 | "\n", 70 | "Note: Try running your function with the number 0 as the input, and see what you get!\n", 71 | "\n" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 2, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "False\n", 84 | "True\n", 85 | "True\n", 86 | "False\n" 87 | ] 88 | } 89 | ], 90 | "source": [ 91 | "def is_power_of_two(n):\n", 92 | " # Check if the number can be divided by two without a remainder\n", 93 | " if n == 0:\n", 94 | " return False\n", 95 | " while n % 2 == 0:\n", 96 | " n = n / 2\n", 97 | " # If after dividing by two the number is 1, it's a power of two\n", 98 | " if n == 1:\n", 99 | " return True\n", 100 | " return False\n", 101 | " \n", 102 | "\n", 103 | "print(is_power_of_two(0)) # Should be False\n", 104 | "print(is_power_of_two(1)) # Should be True\n", 105 | "print(is_power_of_two(8)) # Should be True\n", 106 | "print(is_power_of_two(9)) # Should be False" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "**Problem:** Fill in the empty function so that it returns the sum of all the divisors of a number, without including it. A divisor is a number that divides into another without a remainder." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 3, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "name": "stdout", 123 | "output_type": "stream", 124 | "text": [ 125 | "6\n", 126 | "16\n" 127 | ] 128 | } 129 | ], 130 | "source": [ 131 | "import math\n", 132 | "def sum_divisors(n):\n", 133 | " sum = 1\n", 134 | " # Return the sum of all divisors of n, not including n\n", 135 | " div = 2\n", 136 | " if n == 0:\n", 137 | " return 0\n", 138 | " \n", 139 | " while div < n/2+1:\n", 140 | " if n % div == 0:\n", 141 | " sum += div\n", 142 | " div += 1\n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " return sum\n", 147 | "\n", 148 | "print(sum_divisors(6)) # Should be 6 (sum of 1+2+3)\n", 149 | "print(sum_divisors(12)) # Should be 16 (sum of 1+2+3+4+6)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "**Problem:** The multiplication_table function prints the results of a number passed to it multiplied by 1 through 5. An additional requirement is that the result is not to exceed 25, which is done with the break statement. Fill in the blanks to complete the function to satisfy these conditions." 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 4, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "name": "stdout", 166 | "output_type": "stream", 167 | "text": [ 168 | "3x1=3\n", 169 | "3x2=6\n", 170 | "3x3=9\n", 171 | "3x4=12\n", 172 | "3x5=15\n", 173 | "5x1=5\n", 174 | "5x2=10\n", 175 | "5x3=15\n", 176 | "5x4=20\n", 177 | "5x5=25\n", 178 | "8x1=8\n", 179 | "8x2=16\n", 180 | "8x3=24\n" 181 | ] 182 | } 183 | ], 184 | "source": [ 185 | "def multiplication_table(number):\n", 186 | "\t# Initialize the starting point of the multiplication table\n", 187 | "\tmultiplier = 1\n", 188 | "\t# Only want to loop through 5\n", 189 | "\twhile multiplier <= 5:\n", 190 | "\t\tresult = number*multiplier \n", 191 | "\t\t# What is the additional condition to exit out of the loop?\n", 192 | "\t\tif result>25:\n", 193 | "\t\t\tbreak\n", 194 | "\t\tprint(str(number) + \"x\" + str(multiplier) + \"=\" + str(result))\n", 195 | "\t\t# Increment the variable for the loop\n", 196 | "\t\tmultiplier += 1\n", 197 | "\n", 198 | "multiplication_table(3) \n", 199 | "# Should print: 3x1=3 3x2=6 3x3=9 3x4=12 3x5=15\n", 200 | "\n", 201 | "multiplication_table(5) \n", 202 | "# Should print: 5x1=5 5x2=10 5x3=15 5x4=20 5x5=25\n", 203 | "\n", 204 | "multiplication_table(8)\t\n", 205 | "# Should print: 8x1=8 8x2=16 8x3=24" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "## Practice Quiz: For Loops" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "**Problem:** Fill in the blanks to make the factorial function return the factorial of n. Then, print the first 10 factorials (from 0 to 9) with the corresponding number. Remember that the factorial of a number is defined as the product of an integer and all integers before it. For example, the factorial of five (5!) is equal to 1*2*3*4*5=120. Also recall that the factorial of zero (0!) is equal to 1.\n" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 5, 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "name": "stdout", 229 | "output_type": "stream", 230 | "text": [ 231 | "0 1\n", 232 | "1 1\n", 233 | "2 2\n", 234 | "3 6\n", 235 | "4 24\n", 236 | "5 120\n", 237 | "6 720\n", 238 | "7 5040\n", 239 | "8 40320\n", 240 | "9 362880\n" 241 | ] 242 | } 243 | ], 244 | "source": [ 245 | "def factorial(n):\n", 246 | " result = 1\n", 247 | " for x in range(2,n+1):\n", 248 | " result *= x\n", 249 | " return result\n", 250 | "for n in range(10):\n", 251 | " print(n, factorial(n))" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "**Problem:** Write a script that prints the first 10 cube numbers (x**3), starting with x=1 and ending with x=10." 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": 6, 264 | "metadata": {}, 265 | "outputs": [ 266 | { 267 | "name": "stdout", 268 | "output_type": "stream", 269 | "text": [ 270 | "1\n", 271 | "8\n", 272 | "27\n", 273 | "64\n", 274 | "125\n", 275 | "216\n", 276 | "343\n", 277 | "512\n", 278 | "729\n", 279 | "1000\n" 280 | ] 281 | } 282 | ], 283 | "source": [ 284 | "for x in range(1,11):\n", 285 | " print(x**3)" 286 | ] 287 | }, 288 | { 289 | "cell_type": "markdown", 290 | "metadata": {}, 291 | "source": [ 292 | "**Problem:** Write a script that prints the multiples of 7 between 0 and 100. Print one multiple per line and avoid printing any numbers that aren't multiples of 7. Remember that 0 is also a multiple of 7." 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 7, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "name": "stdout", 302 | "output_type": "stream", 303 | "text": [ 304 | "0\n", 305 | "7\n", 306 | "14\n", 307 | "21\n", 308 | "28\n", 309 | "35\n", 310 | "42\n", 311 | "49\n", 312 | "56\n", 313 | "63\n", 314 | "70\n", 315 | "77\n", 316 | "84\n", 317 | "91\n", 318 | "98\n" 319 | ] 320 | } 321 | ], 322 | "source": [ 323 | "for i in range(0,101,7):\n", 324 | " print(i)" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": {}, 330 | "source": [ 331 | "**Problem:** The retry function tries to execute an operation that might fail, it retries the operation for a number of attempts. Currently the code will keep executing the function even if it succeeds. Modify the code so that it stops trying after the operation succeeded." 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": [ 338 | "```python\n", 339 | "def retry(operation, attempts):\n", 340 | " for n in range(attempts):\n", 341 | " if operation():\n", 342 | " print(\"Attempt \" + str(n) + \" succeeded\")\n", 343 | " break\n", 344 | " else:\n", 345 | " print(\"Attempt \" + str(n) + \" failed\")\n", 346 | "retry(create_user, 3)\n", 347 | "retry(stop_service, 5)\n", 348 | "```" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "N.B. The parameters to retry() are passed by coursera. So I can't run it in the cell" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "## Practice Quiz: Recursion" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "**Problem:** Fill in the blanks to make the is_power_of function return whether the number is a power of the given base. Note: base is assumed to be a positive number. Tip: for functions that return a boolean value, you can return the result of a comparison." 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 8, 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "name": "stdout", 379 | "output_type": "stream", 380 | "text": [ 381 | "True\n", 382 | "True\n", 383 | "False\n" 384 | ] 385 | } 386 | ], 387 | "source": [ 388 | "def is_power_of(number, base):\n", 389 | " # Base case: when number is smaller than base.\n", 390 | " if number < base:\n", 391 | " # If number is equal to 1, it's a power (base**0).\n", 392 | " return number == 1\n", 393 | "\n", 394 | " # Recursive case: keep dividing number by base.\n", 395 | " return is_power_of(number/base, base)\n", 396 | "\n", 397 | "print(is_power_of(8,2)) # Should be True\n", 398 | "print(is_power_of(64,4)) # Should be True\n", 399 | "print(is_power_of(70,10)) # Should be False" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": {}, 405 | "source": [ 406 | "**Problem:** The count_users function recursively counts the amount of users that belong to a group in the company system, by going through each of the members of a group and if one of them is a group, recursively calling the function and counting the members. But it has a bug! Can you spot the problem and fix it?" 407 | ] 408 | }, 409 | { 410 | "cell_type": "markdown", 411 | "metadata": {}, 412 | "source": [ 413 | "```python\n", 414 | "def count_users(group):\n", 415 | " count = 0\n", 416 | " for member in get_members(group):\n", 417 | " count += 1\n", 418 | " if is_group(member):\n", 419 | " count += count_users(member) - 1 \n", 420 | " return count\n", 421 | "print(count_users(\"sales\")) # Should be 3\n", 422 | "print(count_users(\"engineering\")) # Should be 8\n", 423 | "print(count_users(\"everyone\")) # Should be 18<\\code>\n", 424 | "```" 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": {}, 430 | "source": [ 431 | "N.B. The parameters to count_users() are passed by coursera. So, I can't run it in the cell." 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": {}, 437 | "source": [ 438 | "**Problem:** Implement the sum_positive_numbers function, as a recursive function that returns the sum of all positive numbers between the number n received and 1. For example, when n is 3 it should return 1+2+3=6, and when n is 5 it should return 1+2+3+4+5=15." 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": 9, 444 | "metadata": {}, 445 | "outputs": [ 446 | { 447 | "name": "stdout", 448 | "output_type": "stream", 449 | "text": [ 450 | "6\n", 451 | "15\n" 452 | ] 453 | } 454 | ], 455 | "source": [ 456 | "def sum_positive_numbers(n):\n", 457 | " sum = 0\n", 458 | " if n == 1:\n", 459 | " return 1\n", 460 | " else:\n", 461 | " sum += n + sum_positive_numbers(n-1)\n", 462 | " \n", 463 | " return sum\n", 464 | "\n", 465 | "print(sum_positive_numbers(3)) # Should be 6\n", 466 | "print(sum_positive_numbers(5)) # Should be 15" 467 | ] 468 | }, 469 | { 470 | "cell_type": "markdown", 471 | "metadata": {}, 472 | "source": [ 473 | "## Module 3 Graded Assessment" 474 | ] 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "metadata": {}, 479 | "source": [ 480 | "**Problem:** Fill in the blanks of this code to print out the numbers 1 through 7." 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": 10, 486 | "metadata": {}, 487 | "outputs": [ 488 | { 489 | "name": "stdout", 490 | "output_type": "stream", 491 | "text": [ 492 | "1 2 3 4 5 6 7 " 493 | ] 494 | } 495 | ], 496 | "source": [ 497 | "number = 1\n", 498 | "while number <= 7:\n", 499 | "\tprint(number, end=\" \")\n", 500 | "\tnumber += 1" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "**Problem:** The show_letters function should print out each letter of a word on a separate line. Fill in the blanks to make that happen." 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": 11, 513 | "metadata": {}, 514 | "outputs": [ 515 | { 516 | "name": "stdout", 517 | "output_type": "stream", 518 | "text": [ 519 | "H\n", 520 | "e\n", 521 | "l\n", 522 | "l\n", 523 | "o\n" 524 | ] 525 | } 526 | ], 527 | "source": [ 528 | "def show_letters(word):\n", 529 | "\tfor letter in word:\n", 530 | "\t\tprint(letter)\n", 531 | "\n", 532 | "show_letters(\"Hello\")\n", 533 | "# Should print one line per letter" 534 | ] 535 | }, 536 | { 537 | "cell_type": "markdown", 538 | "metadata": {}, 539 | "source": [ 540 | "**Problem:** Complete the function digits(n) that returns how many digits the number has. For example: 25 has 2 digits and 144 has 3 digits. Tip: you can figure out the digits of a number by dividing it by 10 once per digit until there are no digits left." 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 12, 546 | "metadata": {}, 547 | "outputs": [ 548 | { 549 | "name": "stdout", 550 | "output_type": "stream", 551 | "text": [ 552 | "2\n", 553 | "3\n", 554 | "4\n", 555 | "1\n" 556 | ] 557 | } 558 | ], 559 | "source": [ 560 | "import math\n", 561 | "def digits(n):\n", 562 | "\tcount = 0\n", 563 | "\tif n == 0:\n", 564 | "\t return 1\n", 565 | "\twhile (n>0):\n", 566 | "\t\tcount += 1\n", 567 | "\t\tn = math.floor(n/10)\n", 568 | "\treturn count\n", 569 | "\t\n", 570 | "print(digits(25)) # Should print 2\n", 571 | "print(digits(144)) # Should print 3\n", 572 | "print(digits(1000)) # Should print 4\n", 573 | "print(digits(0)) # Should print 1" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "**Problem:** This function prints out a multiplication table (where each number is the result of multiplying the first number of its row by the number at the top of its column). Fill in the blanks so that calling multiplication_table(1, 3) will print out:\n", 581 | "\n", 582 | "1 2 3\n", 583 | "\n", 584 | "2 4 6\n", 585 | "\n", 586 | "3 6 9" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": 13, 592 | "metadata": {}, 593 | "outputs": [ 594 | { 595 | "name": "stdout", 596 | "output_type": "stream", 597 | "text": [ 598 | "1 2 3 \n", 599 | "2 4 6 \n", 600 | "3 6 9 \n" 601 | ] 602 | } 603 | ], 604 | "source": [ 605 | "def multiplication_table(start, stop):\n", 606 | "\tfor x in range(start, stop+1):\n", 607 | "\t\tfor y in range(start, stop+1):\n", 608 | "\t\t\tprint(x*y, end=\" \")\n", 609 | "\t\tprint()\n", 610 | "\n", 611 | "multiplication_table(1, 3)\n", 612 | "# Should print the multiplication table shown above" 613 | ] 614 | }, 615 | { 616 | "cell_type": "markdown", 617 | "metadata": {}, 618 | "source": [ 619 | "**Problem:** The counter function counts down from start to stop when start is bigger than stop, and counts up from start to stop otherwise. Fill in the blanks to make this work correctly.\n", 620 | "\n" 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": 14, 626 | "metadata": {}, 627 | "outputs": [ 628 | { 629 | "name": "stdout", 630 | "output_type": "stream", 631 | "text": [ 632 | "Counting up: 1,2,3,4,5,6,7,8,9,10\n", 633 | "Counting down: 2,1\n", 634 | "Counting up: 5\n" 635 | ] 636 | } 637 | ], 638 | "source": [ 639 | "def counter(start, stop):\n", 640 | "\tx = start\n", 641 | "\tif start > stop:\n", 642 | "\t\treturn_string = \"Counting down: \"\n", 643 | "\t\twhile x >= stop:\n", 644 | "\t\t\treturn_string += str(x)\n", 645 | "\t\t\tif x != stop:\n", 646 | "\t\t\t\treturn_string += \",\"\n", 647 | "\t\t\tx -= 1\n", 648 | "\t\t\t\n", 649 | "\telse:\n", 650 | "\t\treturn_string = \"Counting up: \"\n", 651 | "\t\twhile x <= stop:\n", 652 | "\t\t\treturn_string += str(x)\n", 653 | "\t\t\tif x != stop:\n", 654 | "\t\t\t\treturn_string += \",\"\n", 655 | "\t\t\tx += 1\n", 656 | "\treturn return_string\n", 657 | "\n", 658 | "print(counter(1, 10)) # Should be \"Counting up: 1,2,3,4,5,6,7,8,9,10\"\n", 659 | "print(counter(2, 1)) # Should be \"Counting down: 2,1\"\n", 660 | "print(counter(5, 5)) # Should be \"Counting up: 5\"" 661 | ] 662 | }, 663 | { 664 | "cell_type": "markdown", 665 | "metadata": {}, 666 | "source": [ 667 | "**Problem:** The loop function is similar to range(), but handles the parameters somewhat differently: it takes in 3 parameters: the starting point, the stopping point, and the increment step. When the starting point is greater than the stopping point, it forces the steps to be negative. When, instead, the starting point is less than the stopping point, it forces the step to be positive. Also, if the step is 0, it changes to 1 or -1. The result is returned as a one-line, space-separated string of numbers. For example, loop(11,2,3) should return 11 8 5 and loop(1,5,0) should return 1 2 3 4. Fill in the missing parts to make that happen." 668 | ] 669 | }, 670 | { 671 | "cell_type": "code", 672 | "execution_count": 15, 673 | "metadata": {}, 674 | "outputs": [ 675 | { 676 | "name": "stdout", 677 | "output_type": "stream", 678 | "text": [ 679 | "11 8 5\n", 680 | "1 2 3 4\n", 681 | "-1\n", 682 | "10 12 14 16 18 20 22 24\n", 683 | "\n" 684 | ] 685 | } 686 | ], 687 | "source": [ 688 | "def loop(start, stop, step):\n", 689 | "\treturn_string = \"\"\n", 690 | "\tif step == 0:\n", 691 | "\t\tstep = 1\n", 692 | "\tif start > stop:\n", 693 | "\t\tstep = abs(step) * -1\n", 694 | "\telse:\n", 695 | "\t\tstep = abs(step)\n", 696 | "\tfor count in range(start,stop,step):\n", 697 | "\t\treturn_string += str(count) + \" \"\n", 698 | "\treturn return_string.strip()\n", 699 | "\n", 700 | "print(loop(11,2,3)) # Should be 11 8 5\n", 701 | "print(loop(1,5,0)) # Should be 1 2 3 4\n", 702 | "print(loop(-1,-2,0)) # Should be -1\n", 703 | "print(loop(10,25,-2)) # Should be 10 12 14 16 18 20 22 24 \n", 704 | "print(loop(1,1,1)) # Should be empty" 705 | ] 706 | } 707 | ], 708 | "metadata": { 709 | "kernelspec": { 710 | "display_name": "Python 3", 711 | "language": "python", 712 | "name": "python3" 713 | }, 714 | "language_info": { 715 | "codemirror_mode": { 716 | "name": "ipython", 717 | "version": 3 718 | }, 719 | "file_extension": ".py", 720 | "mimetype": "text/x-python", 721 | "name": "python", 722 | "nbconvert_exporter": "python", 723 | "pygments_lexer": "ipython3", 724 | "version": "3.7.4" 725 | } 726 | }, 727 | "nbformat": 4, 728 | "nbformat_minor": 4 729 | } 730 | -------------------------------------------------------------------------------- /Week4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Practice Quiz: Strings" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The is_palindrome function checks if a string is a palindrome. A palindrome is a string that can be equally read from left to right or right to left, omitting blank spaces, and ignoring capitalization. Examples of palindromes are words like kayak and radar, and phrases like \"Never Odd or Even\". Fill in the blanks in this function to return True if the passed string is a palindrome, False if not." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "True\n", 27 | "False\n", 28 | "True\n" 29 | ] 30 | } 31 | ], 32 | "source": [ 33 | "def is_palindrome(input_string):\n", 34 | "\t# We'll create two strings, to compare them\n", 35 | "\tnew_string = \"\"\n", 36 | "\treverse_string = \"\"\n", 37 | "\t# Traverse through each letter of the input string\n", 38 | "\tfor letter in input_string:\n", 39 | "\t\t# Add any non-blank letters to the \n", 40 | "\t\t# end of one string, and to the front\n", 41 | "\t\t# of the other string. \n", 42 | "\t\tif letter != \" \":\n", 43 | "\t\t\tnew_string += letter.lower() \n", 44 | "\t\t\treverse_string = letter.lower() + reverse_string\n", 45 | "\t# Compare the strings\n", 46 | "\t\n", 47 | "\tif new_string == reverse_string:\n", 48 | "\t\treturn True\n", 49 | "\treturn False\n", 50 | "\n", 51 | "print(is_palindrome(\"Never Odd or Even\")) # Should be True\n", 52 | "print(is_palindrome(\"abc\")) # Should be False\n", 53 | "print(is_palindrome(\"kayak\")) # Should be True" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "Using the format method, fill in the gaps in the convert_distance function so that it returns the phrase \"X miles equals Y km\", with Y having only 1 decimal place. For example, convert_distance(12) should return \"12 miles equals 19.2 km\"." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 2, 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "name": "stdout", 70 | "output_type": "stream", 71 | "text": [ 72 | "12 miles equals 19.2 km\n", 73 | "5.5 miles equals 8.8 km\n", 74 | "11 miles equals 17.6 km\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "def convert_distance(miles):\n", 80 | "\tkm = miles * 1.6 \n", 81 | "\tresult = \"{} miles equals {:.1f} km\".format(miles,km)\n", 82 | "\treturn result\n", 83 | "\n", 84 | "print(convert_distance(12)) # Should be: 12 miles equals 19.2 km\n", 85 | "print(convert_distance(5.5)) # Should be: 5.5 miles equals 8.8 km\n", 86 | "print(convert_distance(11)) # Should be: 11 miles equals 17.6 km" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "Fill in the gaps in the nametag function so that it uses the format method to return first_name and the first initial of last_name followed by a period. For example, nametag(\"Jane\", \"Smith\") should return \"Jane S.\"" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 3, 99 | "metadata": {}, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "Jane S.\n", 106 | "Francesco R.\n", 107 | "Jean-Luc G.\n" 108 | ] 109 | } 110 | ], 111 | "source": [ 112 | "def nametag(first_name, last_name):\n", 113 | "\treturn(\"{} {}.\".format(first_name, last_name[0]))\n", 114 | "\n", 115 | "print(nametag(\"Jane\", \"Smith\")) \n", 116 | "# Should display \"Jane S.\" \n", 117 | "print(nametag(\"Francesco\", \"Rinaldi\")) \n", 118 | "# Should display \"Francesco R.\" \n", 119 | "print(nametag(\"Jean-Luc\", \"Grand-Pierre\")) \n", 120 | "# Should display \"Jean-Luc G.\" " 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "The replace_ending function replaces the old string in a sentence with the new string, but only if the sentence ends with the old string. If there is more than one occurrence of the old string in the sentence, only the one at the end is replaced, not all of them. For example, replace_ending(\"abcabc\", \"abc\", \"xyz\") should return abcxyz, not xyzxyz or xyzabc. The string comparison is case-sensitive, so replace_ending(\"abcabc\", \"ABC\", \"xyz\") should return abcabc (no changes made)." 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 4, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "name": "stdout", 137 | "output_type": "stream", 138 | "text": [ 139 | "It's raining cats and dogs\n", 140 | "She sells seashells by the seashore\n", 141 | "The weather is nice in May\n", 142 | "The weather is nice in April\n" 143 | ] 144 | } 145 | ], 146 | "source": [ 147 | "def replace_ending(sentence, old, new):\n", 148 | "\t# Check if the old string is at the end of the sentence \n", 149 | "\tif old[:] == sentence[-len(old):]:\n", 150 | "\t\t# Using i as the slicing index, combine the part\n", 151 | "\t\t# of the sentence up to the matched string at the \n", 152 | "\t\t# end with the new string\n", 153 | "\t\ti = len(old)\n", 154 | "\t\tnew_sentence = sentence[:-i] + new\n", 155 | "\t\treturn new_sentence\n", 156 | "\n", 157 | "\t# Return the original sentence if there is no match \n", 158 | "\treturn sentence\n", 159 | "\t\n", 160 | "print(replace_ending(\"It's raining cats and cats\", \"cats\", \"dogs\")) \n", 161 | "# Should display \"It's raining cats and dogs\"\n", 162 | "print(replace_ending(\"She sells seashells by the seashore\", \"seashells\", \"donuts\")) \n", 163 | "# Should display \"She sells seashells by the seashore\"\n", 164 | "print(replace_ending(\"The weather is nice in May\", \"may\", \"april\")) \n", 165 | "# Should display \"The weather is nice in May\"\n", 166 | "print(replace_ending(\"The weather is nice in May\", \"May\", \"April\")) \n", 167 | "# Should display \"The weather is nice in April\"" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "## Practice Quiz: Lists" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "Given a list of filenames, we want to rename all the files with extension hpp to the extension h. To do this, we would like to generate a new list called newfilenames, consisting of the new filenames. Fill in the blanks in the code using any of the methods you’ve learned thus far, like a for loop or a list comprehension." 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 5, 187 | "metadata": {}, 188 | "outputs": [ 189 | { 190 | "name": "stdout", 191 | "output_type": "stream", 192 | "text": [ 193 | "[('program.c', 'program.c'), ('stdio.hpp', 'stdio.h'), ('sample.hpp', 'sample.h'), ('a.out', 'a.out'), ('math.hpp', 'math.h'), ('hpp.out', 'hpp.out')]\n" 194 | ] 195 | } 196 | ], 197 | "source": [ 198 | "filenames = [\"program.c\", \"stdio.hpp\", \"sample.hpp\", \"a.out\", \"math.hpp\", \"hpp.out\"]\n", 199 | "newfilenames = []\n", 200 | "for file in filenames:\n", 201 | " if '.hpp' in file:\n", 202 | " newfilenames.append((file,file[:-2]))\n", 203 | " else:\n", 204 | " newfilenames.append((file,file))\n", 205 | "\n", 206 | "print (newfilenames) # Should be [('program.c', 'program.c'), ('stdio.hpp', 'stdio.h'), ('sample.hpp', 'sample.h'), ('a.out', 'a.out'), ('math.hpp', 'math.h'), ('hpp.out', 'hpp.out')]" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "The permissions of a file in a Linux system are split into three sets of three permissions: read, write, and execute for the owner, group, and others. Each of the three values can be expressed as an octal number summing each permission, with 4 corresponding to read, 2 to write, and 1 to execute. Or it can be written with a string using the letters r, w, and x or - when the permission is not granted. For example: 640 is read/write for the owner, read for the group, and no permissions for the others; converted to a string, it would be: \"rw-r-----\" 755 is read/write/execute for the owner, and read/execute for group and others; converted to a string, it would be: \"rwxr-xr-x\" Fill in the blanks to make the code convert a permission in octal format into a string format." 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": 6, 219 | "metadata": {}, 220 | "outputs": [ 221 | { 222 | "name": "stdout", 223 | "output_type": "stream", 224 | "text": [ 225 | "rwxr-xr-x\n", 226 | "rw-r--r--\n", 227 | "rwxr-x---\n", 228 | "rw-------\n" 229 | ] 230 | } 231 | ], 232 | "source": [ 233 | "def octal_to_string(octal):\n", 234 | " result = \"\"\n", 235 | " value_letters = [(4,\"r\"),(2,\"w\"),(1,\"x\")]\n", 236 | " # Iterate over each of the digits in octal\n", 237 | " for digit in [int(n) for n in str(octal)]:\n", 238 | " # Check for each of the permissions values\n", 239 | " for value, letter in value_letters:\n", 240 | " if digit >= value:\n", 241 | " result += letter\n", 242 | " digit -= value\n", 243 | " else:\n", 244 | " result += '-'\n", 245 | " return result\n", 246 | " \n", 247 | "print(octal_to_string(755)) # Should be rwxr-xr-x\n", 248 | "print(octal_to_string(644)) # Should be rw-r--r--\n", 249 | "print(octal_to_string(750)) # Should be rwxr-x---\n", 250 | "print(octal_to_string(600)) # Should be rw-------" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "Let's create a function that turns text into pig latin: a simple text transformation that modifies each word moving the first character to the end and appending \"ay\" to the end. For example, python ends up as ythonpay." 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 7, 263 | "metadata": {}, 264 | "outputs": [ 265 | { 266 | "name": "stdout", 267 | "output_type": "stream", 268 | "text": [ 269 | "ellohay owhay reaay ouyay\n", 270 | "rogrammingpay niay ythonpay siay unfay\n" 271 | ] 272 | } 273 | ], 274 | "source": [ 275 | "def pig_latin(text):\n", 276 | " say = \"\"\n", 277 | " # Separate the text into words\n", 278 | " words = text.split()\n", 279 | " for word in words:\n", 280 | " # Create the pig latin word and add it to the list\n", 281 | " say += word[1:]+word[0]+'ay'\n", 282 | " if word != words[len(words)-1]:\n", 283 | " say +=' '\n", 284 | " # Turn the list back into a phrase\n", 285 | " return say\n", 286 | "\t\t\n", 287 | "print(pig_latin(\"hello how are you\")) # Should be \"ellohay owhay reaay ouyay\"\n", 288 | "print(pig_latin(\"programming in python is fun\")) # Should be \"rogrammingpay niay ythonpay siay unfay\"" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "## Practice Quiz: Dictionaries" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": {}, 301 | "source": [ 302 | "The email_list function receives a dictionary, which contains domain names as keys, and a list of users as values. Fill in the blanks to generate a list that contains complete email addresses (e.g. diana.prince@gmail.com)." 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": 8, 308 | "metadata": {}, 309 | "outputs": [ 310 | { 311 | "name": "stdout", 312 | "output_type": "stream", 313 | "text": [ 314 | "['clark.kent@gmail.com', 'diana.prince@gmail.com', 'peter.parker@gmail.com', 'barbara.gordon@yahoo.com', 'jean.grey@yahoo.com', 'bruce.wayne@hotmail.com']\n" 315 | ] 316 | } 317 | ], 318 | "source": [ 319 | "def email_list(domains):\n", 320 | "\temails = []\n", 321 | "\tfor domain,users in domains.items():\n", 322 | "\t for user in users:\n", 323 | "\t emails.append(user+'@'+domain)\n", 324 | "\treturn(emails)\n", 325 | "\n", 326 | "print(email_list({\"gmail.com\": [\"clark.kent\", \"diana.prince\", \"peter.parker\"], \"yahoo.com\": [\"barbara.gordon\", \"jean.grey\"], \"hotmail.com\": [\"bruce.wayne\"]}))" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": {}, 332 | "source": [ 333 | "The groups_per_user function receives a dictionary, which contains group names with the list of users. Users can belong to multiple groups. Fill in the blanks to return a dictionary with the users as keys and a list of their groups as values." 334 | ] 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": 9, 339 | "metadata": {}, 340 | "outputs": [ 341 | { 342 | "name": "stdout", 343 | "output_type": "stream", 344 | "text": [ 345 | "{'admin': ['local', 'public', 'administrator'], 'userA': ['local'], 'userB': ['public']}\n" 346 | ] 347 | } 348 | ], 349 | "source": [ 350 | "def groups_per_user(group_dictionary):\n", 351 | "\tuser_groups = {}\n", 352 | "\t# Go through group_dictionary\n", 353 | "\tfor group in group_dictionary.keys():\n", 354 | "\t\t# Now go through the users in the group\n", 355 | "\t\tfor users in group_dictionary[group]:\n", 356 | "\t\t lst = []\n", 357 | "\t\t for group in group_dictionary.keys():\n", 358 | "\t\t if users in group_dictionary[group] and users not in lst:\n", 359 | "\t\t lst.append(group)\n", 360 | "\t\t user_groups[users] = lst\n", 361 | "\treturn user_groups\n", 362 | "\t\t\t# Now add the group to the the list of\n", 363 | "# groups for this user, creating the entry\n", 364 | "# in the dictionary if necessary\n", 365 | "\n", 366 | "\n", 367 | "print(groups_per_user({\"local\": [\"admin\", \"userA\"],\n", 368 | "\t\t\"public\": [\"admin\", \"userB\"],\n", 369 | "\t\t\"administrator\": [\"admin\"] }))" 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "metadata": {}, 375 | "source": [ 376 | "## Module 4 Graded Assessment\n" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "The format_address function separates out parts of the address string into new strings: house_number and street_name, and returns: \"house number X on street named Y\". The format of the input string is: numeric house number, followed by the street name which may contain numbers, but never by themselves, and could be several words long. For example, \"123 Main Street\", \"1001 1st Ave\", or \"55 North Center Drive\". Fill in the gaps to complete this function." 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 10, 389 | "metadata": {}, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "house number 123 on street named Main Street\n", 396 | "house number 1001 on street named 1st Ave\n", 397 | "house number 55 on street named North Center Drive\n" 398 | ] 399 | } 400 | ], 401 | "source": [ 402 | "def format_address(address_string):\n", 403 | " # Declare variables\n", 404 | " words = address_string.split()\n", 405 | " num = \"\"\n", 406 | " nam = \"\"\n", 407 | " # Separate the address string into parts\n", 408 | "\n", 409 | " # Traverse through the address parts\n", 410 | " for i,word in enumerate(words):\n", 411 | " if i == 0:\n", 412 | " num += word\n", 413 | " elif i != len(words)-1:\n", 414 | " nam += word +\" \"\n", 415 | " else:\n", 416 | " nam += word\n", 417 | " # Determine if the address part is the\n", 418 | " # house number or part of the street name\n", 419 | "\n", 420 | " # Does anything else need to be done \n", 421 | " # before returning the result?\n", 422 | " \n", 423 | " # Return the formatted string \n", 424 | " return \"house number {} on street named {}\".format(num,nam)\n", 425 | "\n", 426 | "print(format_address(\"123 Main Street\"))\n", 427 | "# Should print: \"house number 123 on street named Main Street\"\n", 428 | "\n", 429 | "print(format_address(\"1001 1st Ave\"))\n", 430 | "# Should print: \"house number 1001 on street named 1st Ave\"\n", 431 | "\n", 432 | "print(format_address(\"55 North Center Drive\"))\n", 433 | "# Should print \"house number 55 on street named North Center Drive\"\n" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "The highlight_word function changes the given word in a sentence to its upper-case version. For example, highlight_word(\"Have a nice day\", \"nice\") returns \"Have a NICE day\". Can you write this function in just one line?" 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": 11, 446 | "metadata": {}, 447 | "outputs": [ 448 | { 449 | "name": "stdout", 450 | "output_type": "stream", 451 | "text": [ 452 | "Have a NICE day\n", 453 | "Shhh, don't be so LOUD!\n", 454 | "Automating with Python is FUN\n" 455 | ] 456 | } 457 | ], 458 | "source": [ 459 | "def highlight_word(sentence, word):\n", 460 | " ret = \"\"\n", 461 | " l = len(word)\n", 462 | " words = sentence.split()\n", 463 | " for i,w in enumerate(words):\n", 464 | " if w[:l] == word:\n", 465 | " ret += w[:l].upper()+w[l:]\n", 466 | " else:\n", 467 | " ret += w\n", 468 | " if i != len(words)-1:\n", 469 | " ret += \" \"\n", 470 | " return ret\n", 471 | "\n", 472 | "print(highlight_word(\"Have a nice day\", \"nice\"))\n", 473 | "print(highlight_word(\"Shhh, don't be so loud!\", \"loud\"))\n", 474 | "print(highlight_word(\"Automating with Python is fun\", \"fun\"))\n" 475 | ] 476 | }, 477 | { 478 | "cell_type": "markdown", 479 | "metadata": {}, 480 | "source": [ 481 | "A professor with two assistants, Jamie and Drew, wants an attendance list of the students, in the order that they arrived in the classroom. Drew was the first one to note which students arrived, and then Jamie took over. After the class, they each entered their lists into the computer and emailed them to the professor, who needs to combine them into one, in the order of each student's arrival. Jamie emailed a follow-up, saying that her list is in reverse order. Complete the steps to combine them into one list as follows: the contents of Drew's list, followed by Jamie's list in reverse order, to get an accurate list of the students as they arrived." 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": 12, 487 | "metadata": {}, 488 | "outputs": [ 489 | { 490 | "name": "stdout", 491 | "output_type": "stream", 492 | "text": [ 493 | "['Mike', 'Carol', 'Greg', 'Marcia', 'Peter', 'Jan', 'Bobby', 'Cindy', 'Alice']\n" 494 | ] 495 | } 496 | ], 497 | "source": [ 498 | "def combine_lists(list1, list2):\n", 499 | " # Generate a new list containing the elements of list2\n", 500 | " for i in range(len(list1)-1,-1,-1):\n", 501 | " list2.append(list1[i])\n", 502 | " return list2\n", 503 | " # Followed by the elements of list1 in reverse order\n", 504 | " \n", 505 | "\t\n", 506 | "Jamies_list = [\"Alice\", \"Cindy\", \"Bobby\", \"Jan\", \"Peter\"]\n", 507 | "Drews_list = [\"Mike\", \"Carol\", \"Greg\", \"Marcia\"]\n", 508 | "\n", 509 | "print(combine_lists(Jamies_list, Drews_list))\n" 510 | ] 511 | }, 512 | { 513 | "cell_type": "markdown", 514 | "metadata": {}, 515 | "source": [ 516 | "Use a list comprehension to create a list of squared numbers (n*n). The function receives the variables start and end, and returns a list of squares of consecutive numbers between start and end inclusively. For example, squares(2, 3) should return [4, 9]." 517 | ] 518 | }, 519 | { 520 | "cell_type": "code", 521 | "execution_count": 13, 522 | "metadata": {}, 523 | "outputs": [ 524 | { 525 | "name": "stdout", 526 | "output_type": "stream", 527 | "text": [ 528 | "[4, 9]\n", 529 | "[1, 4, 9, 16, 25]\n", 530 | "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\n" 531 | ] 532 | } 533 | ], 534 | "source": [ 535 | "def squares(start, end):\n", 536 | "\treturn [x*x for x in range(start,end+1)]\n", 537 | "\n", 538 | "print(squares(2, 3)) # Should be [4, 9]\n", 539 | "print(squares(1, 5)) # Should be [1, 4, 9, 16, 25]\n", 540 | "print(squares(0, 10)) # Should be [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]" 541 | ] 542 | }, 543 | { 544 | "cell_type": "markdown", 545 | "metadata": {}, 546 | "source": [ 547 | "Complete the code to iterate through the keys and values of the car_prices dictionary, printing out some information about each one." 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "execution_count": 14, 553 | "metadata": {}, 554 | "outputs": [ 555 | { 556 | "name": "stdout", 557 | "output_type": "stream", 558 | "text": [ 559 | "Kia Soul costs 19000 dollars\n", 560 | "Lamborghini Diablo costs 55000 dollars\n", 561 | "Ford Fiesta costs 13000 dollars\n", 562 | "Toyota Prius costs 24000 dollars\n", 563 | "\n" 564 | ] 565 | } 566 | ], 567 | "source": [ 568 | "def car_listing(car_prices):\n", 569 | " result = \"\"\n", 570 | " for car,price in car_prices.items():\n", 571 | " result += \"{} costs {} dollars\".format(car,price) + \"\\n\"\n", 572 | " return result\n", 573 | "\n", 574 | "print(car_listing({\"Kia Soul\":19000, \"Lamborghini Diablo\":55000, \"Ford Fiesta\":13000, \"Toyota Prius\":24000}))" 575 | ] 576 | }, 577 | { 578 | "cell_type": "markdown", 579 | "metadata": {}, 580 | "source": [ 581 | "Taylor and Rory are hosting a party. They sent out invitations, and each one collected responses into dictionaries, with names of their friends and how many guests each friend is bringing. Each dictionary is a partial list, but Rory's list has more current information about the number of guests. Fill in the blanks to combine both dictionaries into one, with each friend listed only once, and the number of guests from Rory's dictionary taking precedence, if a name is included in both dictionaries. Then print the resulting dictionary." 582 | ] 583 | }, 584 | { 585 | "cell_type": "code", 586 | "execution_count": 15, 587 | "metadata": {}, 588 | "outputs": [ 589 | { 590 | "name": "stdout", 591 | "output_type": "stream", 592 | "text": [ 593 | "{'David': 1, 'Nancy': 1, 'Robert': 4, 'Adam': 2, 'Samantha': 3, 'Chris': 5, 'Brenda': 3, 'Jose': 3, 'Charlotte': 2, 'Terry': 1}\n" 594 | ] 595 | } 596 | ], 597 | "source": [ 598 | "def combine_guests(guests1, guests2):\n", 599 | " # Combine both dictionaries into one, with each key listed\n", 600 | " # only once, and the value from guests1 taking precedence\n", 601 | " for key,val in guests1.items():\n", 602 | " guests2[key] = val\n", 603 | " return guests2\n", 604 | "\n", 605 | "Rorys_guests = { \"Adam\":2, \"Brenda\":3, \"David\":1, \"Jose\":3, \"Charlotte\":2, \"Terry\":1, \"Robert\":4}\n", 606 | "Taylors_guests = { \"David\":4, \"Nancy\":1, \"Robert\":2, \"Adam\":1, \"Samantha\":3, \"Chris\":5}\n", 607 | "\n", 608 | "print(combine_guests(Rorys_guests, Taylors_guests))\n" 609 | ] 610 | }, 611 | { 612 | "cell_type": "markdown", 613 | "metadata": {}, 614 | "source": [ 615 | "Use a dictionary to count the frequency of letters in the input string. Only letters should be counted, not blank spaces, numbers, or punctuation. Upper case should be considered the same as lower case. For example, count_letters(\"This is a sentence.\") should return {'t': 2, 'h': 1, 'i': 2, 's': 3, 'a': 1, 'e': 3, 'n': 2, 'c': 1}." 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 16, 621 | "metadata": {}, 622 | "outputs": [ 623 | { 624 | "name": "stdout", 625 | "output_type": "stream", 626 | "text": [ 627 | "{'a': 2, 'b': 2, 'c': 2}\n", 628 | "{'m': 1, 'a': 1, 't': 1, 'h': 1, 'i': 1, 's': 1, 'f': 1, 'u': 1, 'n': 1}\n", 629 | "{'t': 2, 'h': 1, 'i': 2, 's': 3, 'a': 1, 'e': 3, 'n': 2, 'c': 1}\n" 630 | ] 631 | } 632 | ], 633 | "source": [ 634 | "def count_letters(text):\n", 635 | " result = {}\n", 636 | " # Go through each letter in the text\n", 637 | " for letter in text:\n", 638 | " # Check if the letter needs to be counted or not\n", 639 | " letter = letter.lower()\n", 640 | " if letter.isalpha():\n", 641 | " if letter in result:\n", 642 | " result[letter] += 1\n", 643 | " else:\n", 644 | " result[letter] = 1\n", 645 | " # Add or increment the value in the dictionary\n", 646 | " return result\n", 647 | "\n", 648 | "print(count_letters(\"AaBbCc\"))\n", 649 | "# Should be {'a': 2, 'b': 2, 'c': 2}\n", 650 | "\n", 651 | "print(count_letters(\"Math is fun! 2+2=4\"))\n", 652 | "# Should be {'m': 1, 'a': 1, 't': 1, 'h': 1, 'i': 1, 's': 1, 'f': 1, 'u': 1, 'n': 1}\n", 653 | "\n", 654 | "print(count_letters(\"This is a sentence.\"))\n", 655 | "# Should be {'t': 2, 'h': 1, 'i': 2, 's': 3, 'a': 1, 'e': 3, 'n': 2, 'c': 1}" 656 | ] 657 | } 658 | ], 659 | "metadata": { 660 | "kernelspec": { 661 | "display_name": "Python 3", 662 | "language": "python", 663 | "name": "python3" 664 | }, 665 | "language_info": { 666 | "codemirror_mode": { 667 | "name": "ipython", 668 | "version": 3 669 | }, 670 | "file_extension": ".py", 671 | "mimetype": "text/x-python", 672 | "name": "python", 673 | "nbconvert_exporter": "python", 674 | "pygments_lexer": "ipython3", 675 | "version": "3.7.4" 676 | } 677 | }, 678 | "nbformat": 4, 679 | "nbformat_minor": 4 680 | } 681 | -------------------------------------------------------------------------------- /Week5.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Classes and Methods" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "***Instance Methods***\n", 15 | "\n", 16 | "**Problem** OK, now it’s your turn! Have a go at writing methods for a class. Create a Dog class with dog_years based on the Piglet class shown before (one human year is about 7 dog years)." 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "21\n" 29 | ] 30 | } 31 | ], 32 | "source": [ 33 | "class Dog:\n", 34 | " years = 0\n", 35 | " def dog_years(self):\n", 36 | " return self.years*7\n", 37 | " \n", 38 | "fido=Dog()\n", 39 | "fido.years=3\n", 40 | "print(fido.dog_years())" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "***Constructors and Other Special Methods***\n", 48 | "\n", 49 | "***Problem*** In this code, there's a Person class that has an attribute name, which gets set when constructing the object. Fill in the blanks so that 1) when an instance of the class is created, the attribute gets set correctly, and 2) when the greeting() method is called, the greeting states the assigned name.\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "hi, my name is Foysal\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "class Person:\n", 67 | " def __init__(self, name):\n", 68 | " self.name = name\n", 69 | " def greeting(self):\n", 70 | " # Should return \"hi, my name is \" followed by the name of the Person.\n", 71 | " return \"hi, my name is {}\".format(self.name)\n", 72 | "\n", 73 | "# Create a new instance with a name of your choice\n", 74 | "some_person = Person(\"Foysal\") \n", 75 | "# Call the greeting method\n", 76 | "print(some_person.greeting())" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "***Inheritance***\n", 84 | "\n", 85 | "***Problem***: Let’s create a new class together and inherit from it. Below we have a base class called Clothing. Together, let’s create a second class, called Shirt, that inherits methods from the Clothing class. Fill in the blanks to make it work properly." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 3, 91 | "metadata": {}, 92 | "outputs": [ 93 | { 94 | "name": "stdout", 95 | "output_type": "stream", 96 | "text": [ 97 | "This Polo is made of Cotton\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "class Clothing:\n", 103 | " material = \"\"\n", 104 | " def __init__(self,name):\n", 105 | " self.name = name\n", 106 | " def checkmaterial(self):\n", 107 | "\t print(\"This {} is made of {}\".format(self.name,self.material))\n", 108 | "\t\t\t\n", 109 | "class Shirt(Clothing):\n", 110 | " material=\"Cotton\"\n", 111 | "\n", 112 | "polo = Shirt(\"Polo\")\n", 113 | "polo.checkmaterial()" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "***Composition***\n", 121 | "\n", 122 | "***Problem***: Your mission: Finish the \"Stock_by_Material\" method and iterate over the amount of each item of a given material that is in stock. When you’re finished, the script should add up to 10 cotton Polo shirts.\n", 123 | "\n" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 4, 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "name": "stdout", 133 | "output_type": "stream", 134 | "text": [ 135 | "10\n" 136 | ] 137 | } 138 | ], 139 | "source": [ 140 | "class Clothing:\n", 141 | " stock={ 'name': [],'material' :[], 'amount':[]}\n", 142 | " def __init__(self,name):\n", 143 | " material = \"\"\n", 144 | " self.name = name\n", 145 | " def add_item(self, name, material, amount):\n", 146 | " Clothing.stock['name'].append(self.name)\n", 147 | " Clothing.stock['material'].append(self.material)\n", 148 | " Clothing.stock['amount'].append(amount)\n", 149 | " def Stock_by_Material(self, material):\n", 150 | " count=0\n", 151 | " n=0\n", 152 | " for item in Clothing.stock['material']:\n", 153 | " if item == material:\n", 154 | " count += Clothing.stock['amount'][n]\n", 155 | " n+=1\n", 156 | " return count\n", 157 | "\n", 158 | "class shirt(Clothing):\n", 159 | " material=\"Cotton\"\n", 160 | "class pants(Clothing):\n", 161 | " material=\"Cotton\"\n", 162 | " \n", 163 | "polo = shirt(\"Polo\")\n", 164 | "sweatpants = pants(\"Sweatpants\")\n", 165 | "polo.add_item(polo.name, polo.material, 4)\n", 166 | "sweatpants.add_item(sweatpants.name, sweatpants.material, 6)\n", 167 | "current_stock = polo.Stock_by_Material(\"Cotton\")\n", 168 | "print(current_stock)" 169 | ] 170 | } 171 | ], 172 | "metadata": { 173 | "kernelspec": { 174 | "display_name": "Python 3", 175 | "language": "python", 176 | "name": "python3" 177 | }, 178 | "language_info": { 179 | "codemirror_mode": { 180 | "name": "ipython", 181 | "version": 3 182 | }, 183 | "file_extension": ".py", 184 | "mimetype": "text/x-python", 185 | "name": "python", 186 | "nbconvert_exporter": "python", 187 | "pygments_lexer": "ipython3", 188 | "version": "3.7.4" 189 | } 190 | }, 191 | "nbformat": 4, 192 | "nbformat_minor": 4 193 | } 194 | -------------------------------------------------------------------------------- /Week5_C1M5L2_Methods_and_Classes_V3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Practice Notebook: Methods and Classes" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The code below defines an *Elevator* class. The elevator has a current floor, it also has a top and a bottom floor that are the minimum and maximum floors it can go to. Fill in the blanks to make the elevator go through the floors requested." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "class Elevator:\n", 24 | " def __init__(self, bottom, top, current):\n", 25 | " \"\"\"Initializes the Elevator instance.\"\"\"\n", 26 | " self.bottom = bottom\n", 27 | " self.top = top\n", 28 | " self.current = current\n", 29 | " def __str__(self):\n", 30 | " \"\"\"Information about Current floor\"\"\"\n", 31 | " return \"Current floor: {}\".format(self.current)\n", 32 | " \n", 33 | " def up(self):\n", 34 | " \"\"\"Makes the elevator go up one floor.\"\"\"\n", 35 | " if self.current < 10:\n", 36 | " self.current += 1\n", 37 | " def down(self):\n", 38 | " \"\"\"Makes the elevator go down one floor.\"\"\"\n", 39 | " if self.current > 0:\n", 40 | " self.current -= 1\n", 41 | " def go_to(self, floor):\n", 42 | " \"\"\"Makes the elevator go to the specific floor.\"\"\"\n", 43 | " if floor >= self.bottom and floor <= self.top:\n", 44 | " self.current = floor\n", 45 | " elif floor < 0:\n", 46 | " self.current = 0\n", 47 | " else:\n", 48 | " self.current = 10\n", 49 | "\n", 50 | "elevator = Elevator(-1, 10, 0)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "This class is pretty empty and doesn't do much. To test whether your *Elevator* class is working correctly, run the code blocks below." 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 2, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "text/plain": [ 68 | "1" 69 | ] 70 | }, 71 | "execution_count": 2, 72 | "metadata": {}, 73 | "output_type": "execute_result" 74 | } 75 | ], 76 | "source": [ 77 | "elevator.up() \n", 78 | "elevator.current #should output 1" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "metadata": {}, 85 | "outputs": [ 86 | { 87 | "data": { 88 | "text/plain": [ 89 | "0" 90 | ] 91 | }, 92 | "execution_count": 3, 93 | "metadata": {}, 94 | "output_type": "execute_result" 95 | } 96 | ], 97 | "source": [ 98 | "elevator.down() \n", 99 | "elevator.current #should output 0" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 4, 105 | "metadata": {}, 106 | "outputs": [ 107 | { 108 | "data": { 109 | "text/plain": [ 110 | "10" 111 | ] 112 | }, 113 | "execution_count": 4, 114 | "metadata": {}, 115 | "output_type": "execute_result" 116 | } 117 | ], 118 | "source": [ 119 | "elevator.go_to(10) \n", 120 | "elevator.current #should output 10" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "If you get a **NameError** message, be sure to run the *Elevator* class definition code block first. If you get an **AttributeError** message, be sure to initialize *self.current* in your *Elevator* class." 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "Once you've made the above methods output 1, 0 and 10, you've successfully coded the *Elevator* class and its methods. Great work!\n", 135 | "

\n", 136 | "For the up and down methods, did you take into account the top and bottom floors? Keep in mind that the elevator shouldn't go above the top floor or below the bottom floor. To check that out, try the code below and verify if it's working as expected. If it's not, then go back and modify the methods so that this code behaves correctly." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 5, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "9\n", 149 | "1\n" 150 | ] 151 | } 152 | ], 153 | "source": [ 154 | "# Go to the top floor. Try to go up, it should stay. Then go down.\n", 155 | "elevator.go_to(10)\n", 156 | "elevator.up()\n", 157 | "elevator.down()\n", 158 | "print(elevator.current) # should be 9\n", 159 | "# Go to the bottom floor. Try to go down, it should stay. Then go up.\n", 160 | "elevator.go_to(-1)\n", 161 | "elevator.down()\n", 162 | "elevator.down()\n", 163 | "elevator.up()\n", 164 | "elevator.up()\n", 165 | "print(elevator.current) # should be 1" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "Now add the __str__ method to your *Elevator* class definition above so that when printing the elevator using the **print( )** method, we get the current floor together with a message. For example, in the 5th floor it should say \"Current floor: 5\"" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 6, 178 | "metadata": {}, 179 | "outputs": [ 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "Current floor: 5\n" 185 | ] 186 | } 187 | ], 188 | "source": [ 189 | "elevator.go_to(5)\n", 190 | "print(elevator)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Remember, Python uses the default method, that prints the position where the object is stored in the computer’s memory. If your output is something like:
\n", 198 | "> <__main__.Elevator object at 0x7ff6a9ff3fd0>\n", 199 | "\n", 200 | "Then you will need to add the special __str__ method, which returns the string that you want to print. Try again until you get the desired output, \"Current floor: 5\"." 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "Once you have successfully produced the desired output, you are all done with this practice notebook. Awesome!" 208 | ] 209 | } 210 | ], 211 | "metadata": { 212 | "kernelspec": { 213 | "display_name": "Python 3", 214 | "language": "python", 215 | "name": "python3" 216 | }, 217 | "language_info": { 218 | "codemirror_mode": { 219 | "name": "ipython", 220 | "version": 3 221 | }, 222 | "file_extension": ".py", 223 | "mimetype": "text/x-python", 224 | "name": "python", 225 | "nbconvert_exporter": "python", 226 | "pygments_lexer": "ipython3", 227 | "version": "3.7.4" 228 | }, 229 | "widgets": { 230 | "application/vnd.jupyter.widget-state+json": { 231 | "state": {}, 232 | "version_major": 2, 233 | "version_minor": 0 234 | } 235 | } 236 | }, 237 | "nbformat": 4, 238 | "nbformat_minor": 4 239 | } 240 | -------------------------------------------------------------------------------- /Week5_C1M5L3_Code_Reuse_V2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Code Reuse" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Let’s put what we learned about code reuse all together. \n", 15 | "

\n", 16 | "First, let’s look back at **inheritance**. Run the following cell that defines a generic `Animal` class. " 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "class Animal:\n", 26 | " name = \"\"\n", 27 | " category = \"\"\n", 28 | " \n", 29 | " def __init__(self, name):\n", 30 | " self.name = name\n", 31 | " \n", 32 | " def set_category(self, category):\n", 33 | " self.category = category" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "What we have is not enough to do much -- yet. That’s where you come in. \n", 41 | "

\n", 42 | "In the next cell, define a `Turtle` class that inherits from the `Animal` class. Then go ahead and set its category. For instance, a turtle is generally considered a reptile. Although modern cladistics call this categorization into question, for purposes of this exercise we will say turtles are reptiles! " 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "class Turtle(Animal):\n", 52 | " category = \"reptile\"" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "Run the following cell to check whether you correctly defined your `Turtle` class and set its category to reptile." 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": {}, 66 | "outputs": [ 67 | { 68 | "name": "stdout", 69 | "output_type": "stream", 70 | "text": [ 71 | "reptile\n" 72 | ] 73 | } 74 | ], 75 | "source": [ 76 | "print(Turtle.category)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "Was the output of the above cell reptile? If not, go back and edit your `Turtle` class making sure that it inherits from the `Animal` class and its category is properly set to reptile. Be sure to re-run that cell once you've finished your edits. Did you get it? If so, great!" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "Next, let’s practice **composition** a little bit. This one will require a second type of `Animal` that is in the same category as the first. For example, since you already created a `Turtle` class, go ahead and create a `Snake` class. Don’t forget that it also inherits from the `Animal` class and that its category should be set to reptile." 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 4, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "class Snake(Animal):\n", 100 | " category = \"reptile\"" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "Now, let’s say we have a large variety of `Animal`s (such as turtles and snakes) in a Zoo. Below we have the `Zoo` class. We’re going to use it to organize our various `Animal`s. Remember, inheritance says a Turtle is an `Animal`, but a `Zoo` is not an `Animal` and an `Animal` is not a `Zoo` -- though they are related to one another. " 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "Fill in the blanks of the `Zoo` class below so that you can use **zoo.add_animal( )** to add instances of the `Animal` subclasses you created above. Once you’ve added them all, you should be able to use **zoo.total_of_category( )** to tell you exactly how many individual `Animal` types the `Zoo` has for each category! Be sure to run the cell once you've finished your edits." 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 5, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "class Zoo:\n", 124 | " def __init__(self):\n", 125 | " self.current_animals = {}\n", 126 | " \n", 127 | " def add_animal(self, animal):\n", 128 | " self.current_animals[animal.name] = animal.category\n", 129 | " \n", 130 | " def total_of_category(self, category):\n", 131 | " result = 0\n", 132 | " for animal in self.current_animals.values():\n", 133 | " if animal == category:\n", 134 | " result += 1\n", 135 | " return result\n", 136 | "\n", 137 | "zoo = Zoo()" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "Run the following cell to check whether you properly filled in the blanks of your `Zoo` class." 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 6, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "name": "stdout", 154 | "output_type": "stream", 155 | "text": [ 156 | "2\n" 157 | ] 158 | } 159 | ], 160 | "source": [ 161 | "turtle = Turtle(\"Turtle\") #create an instance of the Turtle class\n", 162 | "snake = Snake(\"Snake\") #create an instance of the Snake class\n", 163 | "\n", 164 | "zoo.add_animal(turtle)\n", 165 | "zoo.add_animal(snake)\n", 166 | "\n", 167 | "print(zoo.total_of_category(\"reptile\")) #how many zoo animal types in the reptile category" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "Was the output of the above cell 2? If not, go back and edit the `Zoo` class making sure to fill in the blanks with the appropriate attributes. Be sure to re-run that cell once you've finished your edits. \n", 175 | "
\n", 176 | "Did you get it? If so, perfect! You have successfully defined your `Turtle` and `Snake` subclasses as well as your `Zoo` class. You are all done with this notebook. Great work!" 177 | ] 178 | } 179 | ], 180 | "metadata": { 181 | "kernelspec": { 182 | "display_name": "Python 3", 183 | "language": "python", 184 | "name": "python3" 185 | }, 186 | "language_info": { 187 | "codemirror_mode": { 188 | "name": "ipython", 189 | "version": 3 190 | }, 191 | "file_extension": ".py", 192 | "mimetype": "text/x-python", 193 | "name": "python", 194 | "nbconvert_exporter": "python", 195 | "pygments_lexer": "ipython3", 196 | "version": "3.7.4" 197 | } 198 | }, 199 | "nbformat": 4, 200 | "nbformat_minor": 4 201 | } 202 | -------------------------------------------------------------------------------- /Week5_C1M5_Object_Oriented_Programming_V7.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Assessment - Object-oriented programming" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "In this exercise, we'll create a few classes to simulate a server that's taking connections from the outside and then a load balancer that ensures that there are enough servers to serve those connections. \n", 15 | "

\n", 16 | "To represent the servers that are taking care of the connections, we'll use a Server class. Each connection is represented by an id, that could, for example, be the IP address of the computer connecting to the server. For our simulation, each connection creates a random amount of load in the server, between 1 and 10.\n", 17 | "

\n", 18 | "Run the following code that defines this Server class." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "#Begin Portion 1#\n", 28 | "import random\n", 29 | "\n", 30 | "class Server:\n", 31 | " def __init__(self):\n", 32 | " \"\"\"Creates a new server instance, with no active connections.\"\"\"\n", 33 | " self.connections = {}\n", 34 | "\n", 35 | " def add_connection(self, connection_id):\n", 36 | " \"\"\"Adds a new connection to this server.\"\"\"\n", 37 | " connection_load = random.random()*10+1\n", 38 | " # Add the connection to the dictionary with the calculated load\n", 39 | " self.connections[connection_id] = connection_load\n", 40 | " def close_connection(self, connection_id):\n", 41 | " \"\"\"Closes a connection on this server.\"\"\"\n", 42 | " # Remove the connection from the dictionary\n", 43 | " if connection_id in self.connections:\n", 44 | " del self.connections[connection_id]\n", 45 | " def load(self):\n", 46 | " \"\"\"Calculates the current load for all connections.\"\"\"\n", 47 | " total = 0\n", 48 | " for load in self.connections.values():\n", 49 | " total += load\n", 50 | " # Add up the load for each of the connections\n", 51 | " return total\n", 52 | "\n", 53 | " def __str__(self):\n", 54 | " \"\"\"Returns a string with the current load of the server\"\"\"\n", 55 | " return \"{:.2f}%\".format(self.load())\n", 56 | " \n", 57 | "#End Portion 1#" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "Now run the following cell to create a Server instance and add a connection to it, then check the load:" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 2, 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "name": "stdout", 74 | "output_type": "stream", 75 | "text": [ 76 | "5.084033090248539\n" 77 | ] 78 | } 79 | ], 80 | "source": [ 81 | "server = Server()\n", 82 | "server.add_connection(\"192.168.1.1\")\n", 83 | "\n", 84 | "print(server.load())" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "After running the above code cell, if you get a **NameError** message, be sure to run the Server class definition code block first.\n", 92 | "\n", 93 | "The output should be 0. This is because some things are missing from the Server class. So, you'll need to go back and fill in the blanks to make it behave properly. \n", 94 | "

\n", 95 | "Go back to the Server class definition and fill in the missing parts for the `add_connection` and `load` methods to make the cell above print a number different than zero. As the load is calculated randomly, this number should be different each time the code is executed.\n", 96 | "

\n", 97 | "**Hint:** Recall that you can iterate through the values of your connections dictionary just as you would any sequence." 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "Great! If your output is a random number between 1 and 10, you have successfully coded the `add_connection` and `load` methods of the Server class. Well done!\n", 105 | "

\n", 106 | "What about closing a connection? Right now the `close_connection` method doesn't do anything. Go back to the Server class definition and fill in the missing code for the `close_connection` method to make the following code work correctly:" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 3, 112 | "metadata": { 113 | "scrolled": true 114 | }, 115 | "outputs": [ 116 | { 117 | "name": "stdout", 118 | "output_type": "stream", 119 | "text": [ 120 | "0\n" 121 | ] 122 | } 123 | ], 124 | "source": [ 125 | "server.close_connection(\"192.168.1.1\")\n", 126 | "print(server.load())\n" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "You have successfully coded the `close_connection` method if the cell above prints 0.\n", 134 | "

\n", 135 | "**Hint:** Remember that `del` dictionary[key] removes the item with key *key* from the dictionary." 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "Alright, we now have a basic implementation of the server class. Let's look at the basic LoadBalancing class. This class will start with only one server available. When a connection gets added, it will randomly select a server to serve that connection, and then pass on the connection to the server. The LoadBalancing class also needs to keep track of the ongoing connections to be able to close them. This is the basic structure:" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 4, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "#Begin Portion 2#\n", 152 | "class LoadBalancing:\n", 153 | " def __init__(self):\n", 154 | " \"\"\"Initialize the load balancing system with one server\"\"\"\n", 155 | " self.connections = {}\n", 156 | " self.servers = [Server()]\n", 157 | "\n", 158 | " def add_connection(self, connection_id):\n", 159 | " \"\"\"Randomly selects a server and adds a connection to it.\"\"\"\n", 160 | " server = random.choice(self.servers)\n", 161 | " # Add the connection to the dictionary with the selected server\n", 162 | " # Add the connection to the server\n", 163 | " server.add_connection(connection_id)\n", 164 | " self.ensure_availability()\n", 165 | "\n", 166 | " def close_connection(self, connection_id):\n", 167 | " \"\"\"Closes the connection on the the server corresponding to connection_id.\"\"\"\n", 168 | " # Find out the right server\n", 169 | " # Close the connection on the server\n", 170 | " # Remove the connection from the load balancer\n", 171 | " for server in self.servers:\n", 172 | " if connection_id in server.connections:\n", 173 | " server.close_connection(connection_id)\n", 174 | " break\n", 175 | " \n", 176 | " def avg_load(self):\n", 177 | " \"\"\"Calculates the average load of all servers\"\"\"\n", 178 | " # Sum the load of each server and divide by the amount of servers\n", 179 | " total_load = 0\n", 180 | " total_server = 0\n", 181 | " for server in self.servers:\n", 182 | " total_load += server.load()\n", 183 | " total_server += 1\n", 184 | " return total_load/total_server\n", 185 | "\n", 186 | " def ensure_availability(self):\n", 187 | " \"\"\"If the average load is higher than 50, spin up a new server\"\"\"\n", 188 | " if self.avg_load() > 50:\n", 189 | " self.servers.append(Server())\n", 190 | "\n", 191 | " def __str__(self):\n", 192 | " \"\"\"Returns a string with the load for each server.\"\"\"\n", 193 | " loads = [str(server) for server in self.servers]\n", 194 | " return \"[{}]\".format(\",\".join(loads))\n", 195 | "#End Portion 2#" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "As with the Server class, this class is currently incomplete. You need to fill in the gaps to make it work correctly. For example, this snippet should create a connection in the load balancer, assign it to a running server and then the load should be more than zero:" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 5, 208 | "metadata": {}, 209 | "outputs": [ 210 | { 211 | "name": "stdout", 212 | "output_type": "stream", 213 | "text": [ 214 | "10.88706898665605\n" 215 | ] 216 | } 217 | ], 218 | "source": [ 219 | "l = LoadBalancing()\n", 220 | "l.add_connection(\"fdca:83d2::f20d\")\n", 221 | "print(l.avg_load())" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "After running the above code, the output is 0. Fill in the missing parts for the `add_connection` and `avg_load` methods of the LoadBalancing class to make this print the right load. Be sure that the load balancer now has an average load more than 0 before proceeding." 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "What if we add a new server?" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 6, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "name": "stdout", 245 | "output_type": "stream", 246 | "text": [ 247 | "5.443534493328025\n" 248 | ] 249 | } 250 | ], 251 | "source": [ 252 | "l.servers.append(Server())\n", 253 | "print(l.avg_load())" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "The average load should now be half of what it was before. If it's not, make sure you correctly fill in the missing gaps for the `add_connection` and `avg_load` methods so that this code works correctly. \n", 261 | "

\n", 262 | "**Hint:** You can iterate through the all servers in the *self.servers* list to get the total server load amount and then divide by the length of the *self.servers* list to compute the average load amount." 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": {}, 268 | "source": [ 269 | "Fantastic! Now what about closing the connection?" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 7, 275 | "metadata": {}, 276 | "outputs": [ 277 | { 278 | "name": "stdout", 279 | "output_type": "stream", 280 | "text": [ 281 | "0.0\n" 282 | ] 283 | } 284 | ], 285 | "source": [ 286 | "l.close_connection(\"fdca:83d2::f20d\")\n", 287 | "print(l.avg_load())" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "Fill in the code of the LoadBalancing class to make the load go back to zero once the connection is closed.\n", 295 | "

\n", 296 | "Great job! Before, we added a server manually. But we want this to happen automatically when the average load is more than 50%. To make this possible, fill in the missing code for the `ensure_availability` method and call it from the `add_connection` method after a connection has been added. You can test it with the following code:" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": 8, 302 | "metadata": {}, 303 | "outputs": [ 304 | { 305 | "name": "stdout", 306 | "output_type": "stream", 307 | "text": [ 308 | "[50.70%,51.14%,22.51%]\n" 309 | ] 310 | } 311 | ], 312 | "source": [ 313 | "for connection in range(20):\n", 314 | " l.add_connection(connection)\n", 315 | "print(l)" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": {}, 321 | "source": [ 322 | "The code above adds 20 new connections and then prints the loads for each server in the load balancer. If you coded correctly, new servers should have been added automatically to ensure that the average load of all servers is not more than 50%.\n", 323 | "

\n", 324 | "Run the following code to verify that the average load of the load balancer is not more than 50%." 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "execution_count": 9, 330 | "metadata": {}, 331 | "outputs": [ 332 | { 333 | "name": "stdout", 334 | "output_type": "stream", 335 | "text": [ 336 | "41.45121370431007\n" 337 | ] 338 | } 339 | ], 340 | "source": [ 341 | "print(l.avg_load())" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "Awesome! If the average load is indeed less than 50%, you are all done with this assessment." 349 | ] 350 | } 351 | ], 352 | "metadata": { 353 | "kernelspec": { 354 | "display_name": "Python 3", 355 | "language": "python", 356 | "name": "python3" 357 | }, 358 | "language_info": { 359 | "codemirror_mode": { 360 | "name": "ipython", 361 | "version": 3 362 | }, 363 | "file_extension": ".py", 364 | "mimetype": "text/x-python", 365 | "name": "python", 366 | "nbconvert_exporter": "python", 367 | "pygments_lexer": "ipython3", 368 | "version": "3.7.4" 369 | }, 370 | "widgets": { 371 | "application/vnd.jupyter.widget-state+json": { 372 | "state": {}, 373 | "version_major": 2, 374 | "version_minor": 0 375 | } 376 | } 377 | }, 378 | "nbformat": 4, 379 | "nbformat_minor": 4 380 | } 381 | -------------------------------------------------------------------------------- /Week6_C1M6L1_Putting_It_All_Together.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Practice Notebook - Putting It All Together" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Hello, coders! Below we have code similar to what we wrote in the last video. Go ahead and run the following cell that defines our `get_event_date`, `current_users` and `generate_report` methods." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 5, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "def get_event_date(event):\n", 24 | " return event.date\n", 25 | "\n", 26 | "def current_users(events):\n", 27 | " events.sort(key=get_event_date)\n", 28 | " machines = {}\n", 29 | " for event in events:\n", 30 | " if event.machine not in machines:\n", 31 | " machines[event.machine] = set()\n", 32 | " if event.type == \"login\":\n", 33 | " machines[event.machine].add(event.user)\n", 34 | " elif event.type == \"logout\" and event.user in machines[event.machine]:\n", 35 | " machines[event.machine].remove(event.user)\n", 36 | " return machines\n", 37 | "\n", 38 | "def generate_report(machines):\n", 39 | " for machine, users in machines.items():\n", 40 | " if len(users) > 0:\n", 41 | " user_list = \", \".join(users)\n", 42 | " print(\"{}: {}\".format(machine, user_list))" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "No output should be generated from running the custom function definitions above. To check that our code is doing everything it's supposed to do, we need an `Event` class. The code in the next cell below initializes our `Event` class. Go ahead and run this cell next." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 6, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "class Event:\n", 59 | " def __init__(self, event_date, event_type, machine_name, user):\n", 60 | " self.date = event_date\n", 61 | " self.type = event_type\n", 62 | " self.machine = machine_name\n", 63 | " self.user = user" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "Ok, we have an `Event` class that has a constructor and sets the necessary attributes. Next let's create some events and add them to a list by running the following cell." 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 7, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "events = [\n", 80 | " Event('2020-01-21 12:45:56', 'login', 'myworkstation.local', 'jordan'),\n", 81 | " Event('2020-01-22 15:53:42', 'logout', 'webserver.local', 'jordan'),\n", 82 | " Event('2020-01-21 18:53:21', 'login', 'webserver.local', 'lane'),\n", 83 | " Event('2020-01-22 10:25:34', 'logout', 'myworkstation.local', 'jordan'),\n", 84 | " Event('2020-01-21 08:20:01', 'login', 'webserver.local', 'jordan'),\n", 85 | " Event('2020-01-23 11:24:35', 'logout', 'mailserver.local', 'chris'),\n", 86 | "]" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "Now we've got a bunch of events. Let's feed these events into our `custom_users` function and see what happens." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 8, 99 | "metadata": {}, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "{'webserver.local': {'lane'}, 'myworkstation.local': set(), 'mailserver.local': set()}\n" 106 | ] 107 | } 108 | ], 109 | "source": [ 110 | "users = current_users(events)\n", 111 | "print(users)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "Uh oh. The code in the previous cell produces an error message. This is because we have a user in our `events` list that was logged out of a machine he was not logged into. Do you see which user this is? Make edits to the first cell containing our custom function definitions to see if you can fix this error message. There may be more than one way to do so. \n", 119 | "

\n", 120 | "Remember when you have finished making your edits, rerun that cell as well as the cell that feeds the `events` list into our `custom_users` function to see whether the error message has been fixed. Once the error message has been cleared and you have correctly outputted a dictionary with machine names as keys, your custom functions are properly finished. Great!" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "Now try generating the report by running the next cell." 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 9, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "name": "stdout", 137 | "output_type": "stream", 138 | "text": [ 139 | "webserver.local: lane\n" 140 | ] 141 | } 142 | ], 143 | "source": [ 144 | "generate_report(users)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "Whoop whoop! Success! The error message has been cleared and the desired output is produced. You are all done with this practice notebook. Way to go!" 152 | ] 153 | } 154 | ], 155 | "metadata": { 156 | "kernelspec": { 157 | "display_name": "Python 3", 158 | "language": "python", 159 | "name": "python3" 160 | }, 161 | "language_info": { 162 | "codemirror_mode": { 163 | "name": "ipython", 164 | "version": 3 165 | }, 166 | "file_extension": ".py", 167 | "mimetype": "text/x-python", 168 | "name": "python", 169 | "nbconvert_exporter": "python", 170 | "pygments_lexer": "ipython3", 171 | "version": "3.7.4" 172 | } 173 | }, 174 | "nbformat": 4, 175 | "nbformat_minor": 4 176 | } 177 | -------------------------------------------------------------------------------- /Week6_C1M6L2_Final_Project_V3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Final Project - Word Cloud" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "For this project, you'll create a \"word cloud\" from a text by writing a script. This script needs to process the text, remove punctuation, ignore case and words that do not contain all alphabets, count the frequencies, and ignore uninteresting or irrelevant words. A dictionary is the output of the `calculate_frequencies` function. The `wordcloud` module will then generate the image from your dictionary." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "For the input text of your script, you will need to provide a file that contains text only. For the text itself, you can copy and paste the contents of a website you like. Or you can use a site like [Project Gutenberg](https://www.gutenberg.org/) to find books that are available online. You could see what word clouds you can get from famous books, like a Shakespeare play or a novel by Jane Austen. Save this as a .txt file somewhere on your computer.\n", 22 | "

\n", 23 | "Now you will need to upload your input file here so that your script will be able to process it. To do the upload, you will need an uploader widget. Run the following cell to perform all the installs and imports for your word cloud script and uploader widget. It may take a minute for all of this to run and there will be a lot of output messages. But, be patient. Once you get the following final line of output, the code is done executing. Then you can continue on with the rest of the instructions for this notebook.\n", 24 | "

\n", 25 | "**Enabling notebook extension fileupload/extension...**\n", 26 | "
\n", 27 | "**- Validating: OK**" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 1, 33 | "metadata": {}, 34 | "outputs": [ 35 | { 36 | "name": "stdout", 37 | "output_type": "stream", 38 | "text": [ 39 | "Requirement already satisfied: wordcloud in /opt/conda/lib/python3.6/site-packages (1.6.0)\n", 40 | "Requirement already satisfied: pillow in /opt/conda/lib/python3.6/site-packages (from wordcloud) (5.4.1)\n", 41 | "Requirement already satisfied: numpy>=1.6.1 in /opt/conda/lib/python3.6/site-packages (from wordcloud) (1.15.4)\n", 42 | "Requirement already satisfied: matplotlib in /opt/conda/lib/python3.6/site-packages (from wordcloud) (3.0.3)\n", 43 | "Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.6/site-packages (from matplotlib->wordcloud) (0.10.0)\n", 44 | "Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.6/site-packages (from matplotlib->wordcloud) (1.0.1)\n", 45 | "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /opt/conda/lib/python3.6/site-packages (from matplotlib->wordcloud) (2.3.1)\n", 46 | "Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/lib/python3.6/site-packages (from matplotlib->wordcloud) (2.8.0)\n", 47 | "Requirement already satisfied: six in /opt/conda/lib/python3.6/site-packages (from cycler>=0.10->matplotlib->wordcloud) (1.12.0)\n", 48 | "Requirement already satisfied: setuptools in /opt/conda/lib/python3.6/site-packages (from kiwisolver>=1.0.1->matplotlib->wordcloud) (40.8.0)\n", 49 | "Requirement already satisfied: fileupload in /opt/conda/lib/python3.6/site-packages (0.1.5)\n", 50 | "Requirement already satisfied: notebook>=4.2 in /opt/conda/lib/python3.6/site-packages (from fileupload) (5.7.5)\n", 51 | "Requirement already satisfied: traitlets>=4.2 in /opt/conda/lib/python3.6/site-packages (from fileupload) (4.3.2)\n", 52 | "Requirement already satisfied: ipywidgets>=5.1 in /opt/conda/lib/python3.6/site-packages (from fileupload) (7.4.2)\n", 53 | "Requirement already satisfied: ipython-genutils in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (0.2.0)\n", 54 | "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (2.10)\n", 55 | "Requirement already satisfied: tornado<7,>=4.1 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (6.0.2)\n", 56 | "Requirement already satisfied: pyzmq>=17 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (18.0.1)\n", 57 | "Requirement already satisfied: jupyter-client>=5.2.0 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (5.2.4)\n", 58 | "Requirement already satisfied: nbformat in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (4.4.0)\n", 59 | "Requirement already satisfied: jupyter-core>=4.4.0 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (4.4.0)\n", 60 | "Requirement already satisfied: ipykernel in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (5.1.0)\n", 61 | "Requirement already satisfied: prometheus-client in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (0.6.0)\n", 62 | "Requirement already satisfied: Send2Trash in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (1.5.0)\n", 63 | "Requirement already satisfied: terminado>=0.8.1 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (0.8.1)\n", 64 | "Requirement already satisfied: nbconvert in /opt/conda/lib/python3.6/site-packages (from notebook>=4.2->fileupload) (5.4.1)\n", 65 | "Requirement already satisfied: six in /opt/conda/lib/python3.6/site-packages (from traitlets>=4.2->fileupload) (1.12.0)\n", 66 | "Requirement already satisfied: decorator in /opt/conda/lib/python3.6/site-packages (from traitlets>=4.2->fileupload) (4.3.2)\n", 67 | "Requirement already satisfied: ipython>=4.0.0; python_version >= \"3.3\" in /opt/conda/lib/python3.6/site-packages (from ipywidgets>=5.1->fileupload) (7.4.0)\n", 68 | "Requirement already satisfied: widgetsnbextension~=3.4.0 in /opt/conda/lib/python3.6/site-packages (from ipywidgets>=5.1->fileupload) (3.4.2)\n", 69 | "Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/lib/python3.6/site-packages (from jinja2->notebook>=4.2->fileupload) (1.1.1)\n", 70 | "Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/lib/python3.6/site-packages (from jupyter-client>=5.2.0->notebook>=4.2->fileupload) (2.8.0)\n", 71 | "Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in /opt/conda/lib/python3.6/site-packages (from nbformat->notebook>=4.2->fileupload) (3.0.1)\n", 72 | "Requirement already satisfied: mistune>=0.8.1 in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (0.8.4)\n", 73 | "Requirement already satisfied: pygments in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (2.3.1)\n", 74 | "Requirement already satisfied: entrypoints>=0.2.2 in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (0.3)\n", 75 | "Requirement already satisfied: bleach in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (3.1.0)\n", 76 | "Requirement already satisfied: pandocfilters>=1.4.1 in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (1.4.2)\n", 77 | "Requirement already satisfied: testpath in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (0.4.2)\n", 78 | "Requirement already satisfied: defusedxml in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.2->fileupload) (0.5.0)\n", 79 | "Requirement already satisfied: setuptools>=18.5 in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (40.8.0)\n", 80 | "Requirement already satisfied: jedi>=0.10 in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (0.13.3)\n", 81 | "Requirement already satisfied: pickleshare in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (0.7.5)\n", 82 | "Requirement already satisfied: prompt_toolkit<2.1.0,>=2.0.0 in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (2.0.9)\n", 83 | "Requirement already satisfied: backcall in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (0.1.0)\n", 84 | "Requirement already satisfied: pexpect in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (4.6.0)\n", 85 | "Requirement already satisfied: attrs>=17.4.0 in /opt/conda/lib/python3.6/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat->notebook>=4.2->fileupload) (19.1.0)\n", 86 | "Requirement already satisfied: pyrsistent>=0.14.0 in /opt/conda/lib/python3.6/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat->notebook>=4.2->fileupload) (0.14.11)\n", 87 | "Requirement already satisfied: webencodings in /opt/conda/lib/python3.6/site-packages (from bleach->nbconvert->notebook>=4.2->fileupload) (0.5.1)\n", 88 | "Requirement already satisfied: parso>=0.3.0 in /opt/conda/lib/python3.6/site-packages (from jedi>=0.10->ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (0.3.4)\n", 89 | "Requirement already satisfied: wcwidth in /opt/conda/lib/python3.6/site-packages (from prompt_toolkit<2.1.0,>=2.0.0->ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (0.1.7)\n", 90 | "Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/lib/python3.6/site-packages (from pexpect->ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets>=5.1->fileupload) (0.6.0)\n", 91 | "Requirement already satisfied: ipywidgets in /opt/conda/lib/python3.6/site-packages (7.4.2)\n", 92 | "Requirement already satisfied: ipykernel>=4.5.1 in /opt/conda/lib/python3.6/site-packages (from ipywidgets) (5.1.0)\n", 93 | "Requirement already satisfied: widgetsnbextension~=3.4.0 in /opt/conda/lib/python3.6/site-packages (from ipywidgets) (3.4.2)\n", 94 | "Requirement already satisfied: nbformat>=4.2.0 in /opt/conda/lib/python3.6/site-packages (from ipywidgets) (4.4.0)\n", 95 | "Requirement already satisfied: traitlets>=4.3.1 in /opt/conda/lib/python3.6/site-packages (from ipywidgets) (4.3.2)\n", 96 | "Requirement already satisfied: ipython>=4.0.0; python_version >= \"3.3\" in /opt/conda/lib/python3.6/site-packages (from ipywidgets) (7.4.0)\n", 97 | "Requirement already satisfied: tornado>=4.2 in /opt/conda/lib/python3.6/site-packages (from ipykernel>=4.5.1->ipywidgets) (6.0.2)\n", 98 | "Requirement already satisfied: jupyter-client in /opt/conda/lib/python3.6/site-packages (from ipykernel>=4.5.1->ipywidgets) (5.2.4)\n", 99 | "Requirement already satisfied: notebook>=4.4.1 in /opt/conda/lib/python3.6/site-packages (from widgetsnbextension~=3.4.0->ipywidgets) (5.7.5)\n", 100 | "Requirement already satisfied: ipython_genutils in /opt/conda/lib/python3.6/site-packages (from nbformat>=4.2.0->ipywidgets) (0.2.0)\n", 101 | "Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in /opt/conda/lib/python3.6/site-packages (from nbformat>=4.2.0->ipywidgets) (3.0.1)\n", 102 | "Requirement already satisfied: jupyter_core in /opt/conda/lib/python3.6/site-packages (from nbformat>=4.2.0->ipywidgets) (4.4.0)\n", 103 | "Requirement already satisfied: six in /opt/conda/lib/python3.6/site-packages (from traitlets>=4.3.1->ipywidgets) (1.12.0)\n", 104 | "Requirement already satisfied: decorator in /opt/conda/lib/python3.6/site-packages (from traitlets>=4.3.1->ipywidgets) (4.3.2)\n", 105 | "Requirement already satisfied: setuptools>=18.5 in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (40.8.0)\n", 106 | "Requirement already satisfied: jedi>=0.10 in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (0.13.3)\n", 107 | "Requirement already satisfied: pickleshare in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (0.7.5)\n", 108 | "Requirement already satisfied: prompt_toolkit<2.1.0,>=2.0.0 in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (2.0.9)\n", 109 | "Requirement already satisfied: pygments in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (2.3.1)\n", 110 | "Requirement already satisfied: backcall in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (0.1.0)\n", 111 | "Requirement already satisfied: pexpect in /opt/conda/lib/python3.6/site-packages (from ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (4.6.0)\n", 112 | "Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/lib/python3.6/site-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets) (2.8.0)\n", 113 | "Requirement already satisfied: pyzmq>=13 in /opt/conda/lib/python3.6/site-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets) (18.0.1)\n", 114 | "Requirement already satisfied: terminado>=0.8.1 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.8.1)\n", 115 | "Requirement already satisfied: Send2Trash in /opt/conda/lib/python3.6/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (1.5.0)\n", 116 | "Requirement already satisfied: prometheus-client in /opt/conda/lib/python3.6/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.6.0)\n", 117 | "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.6/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (2.10)\n", 118 | "Requirement already satisfied: nbconvert in /opt/conda/lib/python3.6/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (5.4.1)\n", 119 | "Requirement already satisfied: attrs>=17.4.0 in /opt/conda/lib/python3.6/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets) (19.1.0)\n", 120 | "Requirement already satisfied: pyrsistent>=0.14.0 in /opt/conda/lib/python3.6/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets) (0.14.11)\n", 121 | "Requirement already satisfied: parso>=0.3.0 in /opt/conda/lib/python3.6/site-packages (from jedi>=0.10->ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (0.3.4)\n", 122 | "Requirement already satisfied: wcwidth in /opt/conda/lib/python3.6/site-packages (from prompt_toolkit<2.1.0,>=2.0.0->ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (0.1.7)\n", 123 | "Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/lib/python3.6/site-packages (from pexpect->ipython>=4.0.0; python_version >= \"3.3\"->ipywidgets) (0.6.0)\n", 124 | "Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/lib/python3.6/site-packages (from jinja2->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (1.1.1)\n", 125 | "Requirement already satisfied: mistune>=0.8.1 in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.8.4)\n", 126 | "Requirement already satisfied: entrypoints>=0.2.2 in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.3)\n", 127 | "Requirement already satisfied: bleach in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (3.1.0)\n", 128 | "Requirement already satisfied: pandocfilters>=1.4.1 in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (1.4.2)\n", 129 | "Requirement already satisfied: testpath in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.4.2)\n", 130 | "Requirement already satisfied: defusedxml in /opt/conda/lib/python3.6/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.5.0)\n", 131 | "Requirement already satisfied: webencodings in /opt/conda/lib/python3.6/site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.4.0->ipywidgets) (0.5.1)\n", 132 | "Installing /opt/conda/lib/python3.6/site-packages/fileupload/static -> fileupload\n", 133 | "Up to date: /home/jovyan/.local/share/jupyter/nbextensions/fileupload/extension.js\n", 134 | "Up to date: /home/jovyan/.local/share/jupyter/nbextensions/fileupload/widget.js\n", 135 | "Up to date: /home/jovyan/.local/share/jupyter/nbextensions/fileupload/fileupload/widget.js\n", 136 | "- Validating: \u001b[32mOK\u001b[0m\n", 137 | "\n", 138 | " To initialize this nbextension in the browser every time the notebook (or other app) loads:\n", 139 | " \n", 140 | " jupyter nbextension enable fileupload --user --py\n", 141 | " \n", 142 | "Enabling notebook extension fileupload/extension...\n", 143 | " - Validating: \u001b[32mOK\u001b[0m\n" 144 | ] 145 | } 146 | ], 147 | "source": [ 148 | "# Here are all the installs and imports you will need for your word cloud script and uploader widget\n", 149 | "\n", 150 | "!pip install wordcloud\n", 151 | "!pip install fileupload\n", 152 | "!pip install ipywidgets\n", 153 | "!jupyter nbextension install --py --user fileupload\n", 154 | "!jupyter nbextension enable --py fileupload\n", 155 | "\n", 156 | "import wordcloud\n", 157 | "import numpy as np\n", 158 | "from matplotlib import pyplot as plt\n", 159 | "from IPython.display import display\n", 160 | "import fileupload\n", 161 | "import io\n", 162 | "import sys" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "Whew! That was a lot. All of the installs and imports for your word cloud script and uploader widget have been completed. \n", 170 | "

\n", 171 | "**IMPORTANT!** If this was your first time running the above cell containing the installs and imports, you will need save this notebook now. Then under the File menu above, select Close and Halt. When the notebook has completely shut down, reopen it. This is the only way the necessary changes will take affect.\n", 172 | "

\n", 173 | "To upload your text file, run the following cell that contains all the code for a custom uploader widget. Once you run this cell, a \"Browse\" button should appear below it. Click this button and navigate the window to locate your saved text file." 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 17, 179 | "metadata": {}, 180 | "outputs": [ 181 | { 182 | "data": { 183 | "application/vnd.jupyter.widget-view+json": { 184 | "model_id": "074a7c808b4f4bd38e2fb082a3281131", 185 | "version_major": 2, 186 | "version_minor": 0 187 | }, 188 | "text/plain": [ 189 | "FileUploadWidget(label='Browse', _dom_classes=('widget_item', 'btn-group'))" 190 | ] 191 | }, 192 | "metadata": {}, 193 | "output_type": "display_data" 194 | }, 195 | { 196 | "name": "stdout", 197 | "output_type": "stream", 198 | "text": [ 199 | "Uploaded `me.txt` (1.13 kB)\n" 200 | ] 201 | } 202 | ], 203 | "source": [ 204 | "# This is the uploader widget\n", 205 | "\n", 206 | "def _upload():\n", 207 | "\n", 208 | " _upload_widget = fileupload.FileUploadWidget()\n", 209 | "\n", 210 | " def _cb(change):\n", 211 | " global file_contents\n", 212 | " decoded = io.StringIO(change['owner'].data.decode('utf-8'))\n", 213 | " filename = change['owner'].filename\n", 214 | " print('Uploaded `{}` ({:.2f} kB)'.format(\n", 215 | " filename, len(decoded.read()) / 2 **10))\n", 216 | " file_contents = decoded.getvalue()\n", 217 | "\n", 218 | " _upload_widget.observe(_cb, names='data')\n", 219 | " display(_upload_widget)\n", 220 | "\n", 221 | "_upload()" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "The uploader widget saved the contents of your uploaded file into a string object named *file_contents* that your word cloud script can process. This was a lot of preliminary work, but you are now ready to begin your script. " 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "Write a function in the cell below that iterates through the words in *file_contents*, removes punctuation, and counts the frequency of each word. Oh, and be sure to make it ignore word case, words that do not contain all alphabets and boring words like \"and\" or \"the\". Then use it in the `generate_from_frequencies` function to generate your very own word cloud!\n", 236 | "

\n", 237 | "**Hint:** Try storing the results of your iteration in a dictionary before passing them into wordcloud via the `generate_from_frequencies` function." 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 20, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "def calculate_frequencies(file_contents):\n", 247 | " # Here is a list of punctuations and uninteresting words you can use to process your text\n", 248 | " punctuations = '''!()-[]{};:'\"\\,<>./?@#$%^&*_~\"'''\n", 249 | " uninteresting_words = [\"the\", \"a\", \"to\", \"if\", \"is\", \"in\" \"it\", \"of\", \"and\", \"or\",\"on\", \"an\", \"as\", \"i\", \"me\", \"my\", \\\n", 250 | " \"we\", \"our\", \"ours\", \"you\", \"your\", \"yours\", \"he\", \"she\", \"him\", \"his\", \"her\", \"hers\", \"its\", \"they\", \"them\", \\\n", 251 | " \"their\", \"what\", \"which\", \"who\", \"whom\", \"this\", \"that\", \"am\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\", \\\n", 252 | " \"have\", \"has\", \"had\", \"do\", \"does\", \"did\", \"but\", \"at\", \"by\", \"with\", \"from\", \"here\", \"when\", \"where\", \"how\", \\\n", 253 | " \"all\", \"any\", \"both\", \"each\", \"few\", \"more\", \"some\", \"such\", \"no\", \"nor\", \"too\", \"very\", \"can\", \"will\", \"just\"]\n", 254 | " \n", 255 | " # LEARNER CODE START HERE\n", 256 | " frequencies = {}\n", 257 | " taken = []\n", 258 | " for letter in punctuations:\n", 259 | " file_contents = file_contents.replace(letter,'')\n", 260 | " for word in uninteresting_words:\n", 261 | " w = ' '+word+' '\n", 262 | " file_contents = file_contents.replace(w,' ')\n", 263 | " for word in file_contents.split():\n", 264 | " if word.lower() not in taken:\n", 265 | " taken.append(word.lower())\n", 266 | " if word not in frequencies:\n", 267 | " frequencies[word] = 1\n", 268 | " else:\n", 269 | " frequencies[word] += 1\n", 270 | " \n", 271 | " \n", 272 | " #wordcloud\n", 273 | " cloud = wordcloud.WordCloud()\n", 274 | " cloud.generate_from_frequencies(frequencies)\n", 275 | " return cloud.to_array()" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "If you have done everything correctly, your word cloud image should appear after running the cell below. Fingers crossed!" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 21, 288 | "metadata": {}, 289 | "outputs": [ 290 | { 291 | "data": { 292 | "image/png": "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\n", 293 | "text/plain": [ 294 | "
" 295 | ] 296 | }, 297 | "metadata": { 298 | "needs_background": "light" 299 | }, 300 | "output_type": "display_data" 301 | } 302 | ], 303 | "source": [ 304 | "# Display your wordcloud image\n", 305 | "\n", 306 | "myimage = calculate_frequencies(file_contents)\n", 307 | "plt.imshow(myimage, interpolation = 'nearest')\n", 308 | "plt.axis('off')\n", 309 | "plt.show()" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "If your word cloud image did not appear, go back and rework your `calculate_frequencies` function until you get the desired output. Definitely check that you passed your frequecy count dictionary into the `generate_from_frequencies` function of `wordcloud`. Once you have correctly displayed your word cloud image, you are all done with this project. Nice work!" 317 | ] 318 | } 319 | ], 320 | "metadata": { 321 | "coursera": { 322 | "course_slug": "python-crash-course", 323 | "graded_item_id": "Z5d28", 324 | "launcher_item_id": "eSjyd" 325 | }, 326 | "kernelspec": { 327 | "display_name": "Python 3", 328 | "language": "python", 329 | "name": "python3" 330 | }, 331 | "language_info": { 332 | "codemirror_mode": { 333 | "name": "ipython", 334 | "version": 3 335 | }, 336 | "file_extension": ".py", 337 | "mimetype": "text/x-python", 338 | "name": "python", 339 | "nbconvert_exporter": "python", 340 | "pygments_lexer": "ipython3", 341 | "version": "3.7.4" 342 | }, 343 | "widgets": { 344 | "application/vnd.jupyter.widget-state+json": { 345 | "state": {}, 346 | "version_major": 2, 347 | "version_minor": 0 348 | } 349 | } 350 | }, 351 | "nbformat": 4, 352 | "nbformat_minor": 4 353 | } 354 | --------------------------------------------------------------------------------