<jupyter_start><jupyter_text>Exercises  Ex 1 - Getting started with running code in a notebookFirst lets go through some basic examples to explain editing and running of notebook cells. In the Help menu you can find a list of keyboard shortcuts and a tour of the notebook user interface. You don't need to learn all that now, but do take a look later if you intend to use notebooks a lot.  Ex 1a - Moving between cells and running them* When there's a blue box around a cell you can move up and down with arrow keys. Press `Enter` or click in the textbox to start editing the cell.* When there's a green box around a cell you can edit the text in it. `Enter` inserts a new line, `Shift+Enter` runs the code in the cell.Run the following cell by clicking in it then pressing `Shift+Enter`:<jupyter_code>x = 7 * 6
print(x)<jupyter_output><empty_output><jupyter_text>Try the next one as well, see how the variable x defined in the previous cell is available:<jupyter_code>print(x)<jupyter_output><empty_output><jupyter_text>Placing a variable alone on the last line displays it. We'll be using `print(x)` instead for the rest of the examples, since that can be used any place in the cell.Try running this cell:<jupyter_code>x = 3 * 1.2
x<jupyter_output><empty_output><jupyter_text>Now go up the the `print(x)` cell above and run it again. Note that the value of variables is determined by the order you execute the cells, not the order of the cells in the notebook document. The execution order is recorded as `In[4]` to the left.To clear all previously defined variables, use the menu `Kernel -> Restart & Clear output`.  Ex 1b - Debugging a faulty programPrograms are rarely correct on the first try. Here's an attempt at computing total cost of a number of items.* Try running the next cell. Is the printed total correct?* Fix the code so the total is computed correctly and run again.<jupyter_code>price = 15.0
quantity = 3
total = price + quantity
print(total)

a = 10
b = [1, 2, 3, 4, 5]<jupyter_output><empty_output><jupyter_text>Ex 2 - Variables  Ex 2a - Counting with integersLets warm up with some basic integer variables. After each step, use `print` to inspect the variable values. Try to figure out what will be printed _before_ running the cell.* Set a variable `i` to any integer number.* Set another variable `j` to equal `i + 2`.* Add another integer number to `i`. What is the value of `j` now?<jupyter_code># Implement here!<jupyter_output><empty_output><jupyter_text>Ex 2b - Swap two variablesSomeone has mixed up the first and last name here, add code to swap the values of the two variables.<jupyter_code>first = "Trump"
last = "Donald"

# Implement here!
first, last = last, first

print(first, last)<jupyter_output><empty_output><jupyter_text>Ex 3 - Calculations with variables  Ex 3a - Buying fruitGiven the price and quantity of apples and pears provided here, compute* The number of fruits.* The average cost of a fruit.* The total cost of this checkout.Store each in its own variable and print it.<jupyter_code>apples = 7
apple_price = 1.0

pears = 2
pear_price = 1.2

num_fruits = 0
tot_price = 0.0
for n, p in zip((apples, pears),(apple_price, pear_price)):
    num_fruits += n
    tot_price += n*p
print("The total number of fruits is:  {:>7d}    pcs".format(num_fruits))
print("The average cost of a fruit is: {:>10.2f} NOK".format(tot_price/num_fruits))
print("The total cost of checkout is:  {:>10.2f} NOK".format(tot_price))

# Implement here!<jupyter_output><empty_output><jupyter_text>Ex 3b - Integer division and rounding of numbersAs a storage worker, you need to pack some items into crates. Naturally you sit down and write a small python program to figure out how best to do this.* How many crates can be filled completely with the number of items?* Compute the remainder, how many items are left in the last crate?* From the number of filled crates and the remainder, compute how many items you originally had. Does it match the original number of items? If not, you have a bug in your program and need to find and fix it!<jupyter_code>items = 123
crate_size = 20
print("items: {}, crate size: {}".format(items, crate_size))

num_crates = items // crate_size
rest = items % crate_size

print("We need {} full crates".format(num_crates), end="")
if rest:
    print(", and one additional with {} items.".format(rest))
else:
    print(".")
assert crate_size*num_crates + rest == items
# Implement here!<jupyter_output><empty_output><jupyter_text>Ex 3c - Investigating the behaviour of floating point numbersLets try a simple calculation with floating point numbers:* Set a variable to 0.7 (pick your own variable name)* Add 0.1 to the variable* Print the variable<jupyter_code># Implement here!<jupyter_output><empty_output><jupyter_text>Are you surprised by the result? Floating point numbers have high precision but are not exact.* Print the difference between `result` and `0.8` to see approximately how precise these numbers are.<jupyter_code># Print difference between result and 0.8 here<jupyter_output><empty_output><jupyter_text>The difference you just printed should be on a format like `1.23e-9` which means $1.23 \times 10^{-9}$. The exponent of the number you printed is approximately the number of accurate digits in a float.The next cell prints the number 0.7 with only 3 decimals, which gets rounded to 0.700 in the string formatting.* To avoid the rounding, change the number of decimals from 3 to a number larger than the number of accurate digits you found above.<jupyter_code>print("{:.3f}".format(0.7))<jupyter_output><empty_output><jupyter_text>Ex 4 - StringsText string manipulation is used for things like dealing with filenames and paths, reading data from text files, and writing data to text files.  Ex 4a - Adding stringsAdd the first and last name together with a space between to form the full name.<jupyter_code>firstname = "John"
lastname = "Doe"

fullname = " ".join([firstname, lastname])

print(fullname)<jupyter_output><empty_output><jupyter_text>Ex 4b - Splitting and formatting strings* Split `fullname` into two new variables `firstname` and `lastname`, make sure to remove any spaces.* Write a string format `template` to match how James Bond likes to introduce himself.When running the cell, the output should be _My name is Bond. <NAME>._<jupyter_code>fullname = "<NAME>"

# Replace these three lines with your implementation
firstname = fullname.split(",")[1].strip()
lastname = fullname.split(",")[0].strip()
template = "My name is {ln}. {fn} {ln}.".format(ln=lastname, fn=firstname)

print(template.format(first=firstname, last=lastname))<jupyter_output><empty_output><jupyter_text>Ex 5 - LoopsBefore we move on to more complex data structures, lets try some loops.  Ex 5a - Looping over number rangesUsing `range`,* Print the numbers from 0 through 5 (inclusive)* Loop over the numbers from 3 to 10 (not including 10)* Print the even numbers from 4 through 8 (inclusive)<jupyter_code># Implement here
for i in range(6):
    print(i, end=",")
print()
for i in range(3, 10):
    print(i, end=",")
print()
for i in range(4,9,2):
    print(i, end=",")<jupyter_output><empty_output><jupyter_text>Ex 5b - Double until there's no more grains leftIf you give away one grain on the first chessboard square, 2 on the second, 4 on the third, and so on by doubling the number of grains for each square, how many squares until you have given away more than a quadrillion ($10^15$) grains?* Use a while loop to find the answer._Tip: If you get stuck in an infinite loop, it will look like `In [*]` on the side of the cell. If that happens, select the menu "Kernel -> interrupt", fix your code, and try again._<jupyter_code># Implement here
gsum = 0
i = 0
limit = 1e15
while gsum < limit:
    i += 1
    gsum += 2**(i - 1)
print("We need {} squares to reach {:.1e}".format(i, limit))<jupyter_output><empty_output><jupyter_text>Ex 6 - ListsLets get familiar with lists, which form the basis of most structured data processing.|  Ex 6a - Accessing single values from a listHere we define a list with the numbers from 1 up to and including 1000.Print the first, the middle, and the last value from these list.Keep in mind that the first index is `0` and the last is `length of list - 1`.<jupyter_code>values = list(range(1, 1001))
size = len(values)

print(values[0], values[size//2], values[-1])<jupyter_output><empty_output><jupyter_text>Ex 6b - Computing something for each value in a listCompute $sin(x)$ for each value $x$ in the `values` list. Print the value and the computed result together for each value.<jupyter_code>from math import sin, pi
values = [0.0, 0.5*pi, pi, 1.5*pi]

for x in values:
    print("sin({:.2f}) = {:.2f}".format(x, sin(x)))<jupyter_output><empty_output><jupyter_text>Ex 6c - Lists of lists of listsWe have been given some `data` about the sales completed by each member of the sales team.The `data` is a list of lines, where each line is itself a list.Each line contains two items: the `name` of a sales team member, and a list of `sales` made by that person.We want to build a new list from this data.For each salesperson, the new list `summary` should contain a list with name, number of sales, total revenue from all sales made by this person, and a computed bonus that is $20\%$ of the revenue the exceeds 200.```summary = [    ["name", num_sales, total, bonus],]```* To warm up, print the last sale that Ida made by indexing the lists directly. _Hint: `data[1]` is the line for Ida._* Build `summary` from `data`. _Hint: You can loop over the data with_: `for name, sales in data:`.* For each line in the computed stats, print a summary line with the computed information.* Bonus points: find and print who made the largest single sale.<jupyter_code>data = [
    ["Mark", [100, 20, 50, 60]],
    ["Ida", [300, 210]],
    ["Bob", [30, 70, 90, 160, 80]],
    ["Heidi", [10, 110, 200]],
]

print(data[1][1][-1])

summary = []
bonus_limit = 200
bonus_rate = 0.2
best_sale = 0
best_seller = ""
for name, sales in data:
    nsales = len(sales)
    ssum = sum(sales)
    if max(sales) > best_sale:
        best_seller = name
        best_sale = max(sales)
    bonus = 0 if ssum <= bonus_limit else bonus_rate*(ssum - bonus_limit)
    summary.append([name, nsales, ssum, bonus])

for name, nsales, ssum, bonus in summary:
    print("Seller {:6} sold {} items for total value "
          "of {}, earning bonus {:>6.2f}".format(name, nsales, ssum, bonus))
if best_seller:
    print("Seller of the month: {}".format(best_seller))
else:
    print("Fire the sales teeam and hire a new one!")<jupyter_output>210
Seller Mark   sold 4 items for total value of 230, earning bonus   6.00
Seller Ida    sold 2 items for total value of 510, earning bonus  62.00
Seller Bob    sold 5 items for total value of 430, earning bonus  46.00
Seller Heidi  sold 3 items for total value of 320, earning bonus  24.00
Seller of the month: Ida<jupyter_text>Ex 7 - DictsDictionaries hold key,value pairs and allow looking up a value given a key.  Ex 7a - Formatting a string from dict entriesProvided the dict `person` here, print a string formatted with the phone number first, followed by the full name in lastname, firstname format.<jupyter_code>person = {
    "firstname": "Ola",
    "lastname": "Nordmann",
    "address": "Drammensveien 1",
    "phone": "+47 1234567890"
}

# Implement here!
print("{p}: {l}, {f}".format(p=person["phone"], l=person["lastname"],
                             f=person["firstname"]))<jupyter_output>+47 1234567890: Nordmann, Ola<jupyter_text>Ex 7b - Filter products from nested dictsHere's some information about a phone store. You want to find and display the most expensive phone variant from each model, but unfortunately the data input was somewhat messily done and the model names use different case and sometimes contain spaces that makes it harder to compare the strings.* Clean up the products list by normalizing the `"model"` field to be lower case without spaces.* Build a new dict `products_by_model` where the keys are normalized model strings and the values are lists of tuples `(price, variant)`, one for each product with the specific model.* For each model, print the number of products and all product variants sorted by price.<jupyter_code>products = [
    { "model": "iPhone", "variant": "8", "price": 8000 },
    { "model": "Galaxy", "variant": "S6", "price": 4000 },
    { "model": "GALAXY", "variant": "S8", "price": 6000 },
    { "model": "iphone", "variant": "X", "price": 10000 },
    { "model": " galaxy", "variant": "S7", "price": 5000 },
    { "model": "IPHONE ", "variant": "7", "price": 5000 },
]

products_by_model = {}
for p in products:
    model = p["model"].strip().lower()
    if model not in products_by_model.keys():
        products_by_model[model] = list()
    products_by_model[model].append((p["price"], p["variant"]))

for p in products_by_model:
    new_str = "Number of {} variants: {}".format(p, len(products_by_model[p]))
    print(new_str)
    sorted_products = sorted(products_by_model[p])
    for item in sorted_products:
        print("{:8}: {}".format(item[0], item[1]))

for p in products_by_model:
    print("Number of {} variants: {}".format(p, len(products_by_model[p])))
    for price, variant in sorted(products_by_model[p]):
        print("{:8}: {}".format(price, variant))<jupyter_output>Number of iphone variants: 3
    5000: 7
    8000: 8
   10000: X
Number of galaxy variants: 3
    4000: S6
    5000: S7
    6000: S8<jupyter_text>Ex 8 - Conditional branching with if statements  Ex 8a - Odd or even numbersWrite a for loop over the numbers starting at 1 and ending with 10, and print each number labelled as odd or even.<jupyter_code># Implement here
for i in range(1, 11):
    if i % 2:
        label = "odd"
    else:
        label = "even"
    print("{} is {}".format(i, label))

for i in range(1, 11):
    print("{} is {}".format(i, "odd" if i % 2 else "even"))<jupyter_output>1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
10 is even
1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
10 is even<jupyter_text>Ex 8b - Pick your prioritiesSome tasks are important, some are urgent, some are both, and some are neither.Write a program that prints advice for each task topic on what to dobased on whether the task is important and/or urgent.<jupyter_code>tasks = [
    { "topic": "pay overdue invoce",    "important": True,  "urgent": True },
    { "topic": "learn programming",     "important": True,  "urgent": False },
    { "topic": "reply to all emails",   "important": False, "urgent": True },
    { "topic": "sorting pens by color", "important": False, "urgent": False },
]

for task in tasks:
    if task["important"]:
        if task["urgent"]:
            msg = "{} right now.".format(task["topic"].capitalize())
        else:
            msg = "Remember to allocate time to {}.".format(task["topic"])
    else:
        if task["urgent"]:
            msg = "{} sounds like stress, do you really need to do it?".format(task["topic"].capitalize())
        else:
            msg = "{} is just stupid!".format(task["topic"].capitalize())
    print(msg)
# Implement here!<jupyter_output>Pay overdue invoce right now.
Remember to allocate time to learn programming.
Reply to all emails sounds like stress, do you really need to do it?
Sorting pens by color is just stupid!<jupyter_text>Ex 9 - Find names of text files on file system* Get a list of all the files in the current directory using `os.listdir`.* Print the full path of each filename.* Split the full path into the directory name, the file extension, and the root of the filename (the root is what's left when removing directory name and file extension).* Join the directory name, filename root, and file extension together to get back the full path._Hint: `os.path` contains many useful functions. See the documentation on python.org and the tab completion tip below._<jupyter_code># Run this to import the os module which is needed for the exercises below
import os

# Tip: Run this cell to see documentation for the function
# os.path.splitext in a window at the bottom of the page:
os.path.splitext?
# Tip: Place the text cursor after 'os.path' below, then
# type '.' and then 'Tab', to see a list of things available in os.path
os.path
# Implement here
for f in os.listdir():
    full_path = os.path.abspath(f)
    print(full_path)
    pname, fn = os.path.split(full_path)
    root, ext = os.path.splitext(fn)
    full_path_2 = os.path.join(pname, root + ext)
    assert full_path_2 == full_path<jupyter_output>/Users/skavhaug/courses/python/statkraft_intro/notebooks/exercises/.ipynb_checkpoints
/Users/skavhaug/courses/python/statkraft_intro/notebooks/exercises/exercises-day1-ola.ipynb
/Users/skavhaug/courses/python/statkraft_intro/notebooks/exercises/exercises-day1.ipynb
/Users/skavhaug/courses/python/statkraft_intro/notebooks/exercises/README.md
/Users/skavhaug/courses/python/statkraft_intro/notebooks/exercises/Untitled.ipynb
/Users/skavhaug/courses/python/statkraft_intro/notebooks/exercises/Untitled1.ipynb<jupyter_text>Ex 10 - A taste of text encoding problemsFor long winded historical reasons, there is a plethora of standards for how to represent text strings in computers.Strings in python (version 3+) are always _unicode_ strings and can contain most letters from most languages, including fictional ones. You don't have to worry about how these are represented internally.Unfortunately, strings stored as bits and bytes in files on disk can originate from different programs and must be decoded using the same encoding standard they were written with.The `utf-8` encoding can represent the full range of unicode strings.Older standards only included some subset of letters for a specific language.The encodings `cp858` and `cp1252` are common on Norwegian Windows systems.The encoding `cp1253` has Greek letters but not Norwegian ones.* Try decoding the bytestring below using the encoding standards `cp858`, `cp1252`, `utf-8`, and `cp1253` and print the resulting strings. Which is the right one? What happens with the Greek encoding version?_Hint: encode a text string using the notation `mystring.encode("utf-8")` and decode a bytestring using the notation `bytestring.decode("utf-8")`._  These bytes are the result of encoding a text string with one specific encoding standard Implement here<jupyter_code>bytestring = b'\xc3\x98yvind \xc3\x85sen'
encodings = ["cp858", "cp1252", "utf-8", "cp1253"]
for encoding in encodings:
    try:  # Advanced construction that we will cover on day two!
        decoded = bytestring.decode(encoding)
        print("Decoding with {} gives {}".format(encoding, decoded))
        if set("æøåÆØÅ").intersection(decoded):
            print("{} is probably the one you want!".format(encoding))
    except UnicodeDecodeError:
        print("{} is definitely wrong!".format(encoding))<jupyter_output>Decoding with cp858 gives ├ÿyvind ├àsen
Decoding with cp1252 gives Ã˜yvind Ã…sen
Decoding with utf-8 gives Øyvind Åsen
utf-8 is probably the one you want!
cp1253 is definitely wrong!<jupyter_text>Ex 11 - More loopsLets try some more exercises where you need to nest loops and branches.  Ex 11a - Counting overtime hoursYou're running a shop with four employees. Because of poor planning you end up pushing some of your employees to work very hard part of the week. Now you need to compute the number of overtime hours each employee to get the overtime payment correct. Your employees are entitled to overtime payment for all hours above 7.5 any single day.* Print name and total hours worked for each employee.* Compute the number of overtime hours this week for each employee.* Compute the total number of overtime hours for all employees.<jupyter_code># This dict maps employee name to number of hours worked each day for a week
hours_per_day = {
    "Bob":   [12.5, 12.5, 12.5, 0.0, 0.0],
    "Ida":   [10.0, 10.0,  9.0, 4.5, 4.0],
    "Dave":  [ 8.0,  8.5,  7.5, 7.5, 6.0],
    "Marie": [ 7.5,  7.5,  7.5, 7.5, 7.5],
}

normal_hours = 7.5
total_overtimes = 0
for name, hours in hours_per_day.items():
    print("{}\n{}".format(name, len(name)*"="))
    print("Worked a total of {:5.2f} hours last week".format(sum(hours)))
    num_overtimes = 0
    for h in hours:
        num_overtimes += max(0, h - normal_hours)
    total_overtimes += num_overtimes
    print("Had {:5.2f} overtime hours last week\n".format(num_overtimes))

print("You poor employees worked a total of {} overtime hours last week.".format(total_overtimes))
# Implement here<jupyter_output>Bob
===
Worked a total of 37.50 hours last week
Had 15.00 overtime hours last week

Ida
===
Worked a total of 37.50 hours last week
Had  6.50 overtime hours last week

Dave
====
Worked a total of 37.50 hours last week
Had  1.50 overtime hours last week

Marie
=====
Worked a total of 37.50 hours last week
Had  0.00 overtime hours last week

You poor employees worked a total of 23.0 overtime hours last week.
{'fraction_non_alphanumeric': 0.09740444951511694, 'fraction_numerical': 0.024671990872789505, 'mean_word_length': 4.289665577068142, 'pattern_counts': {'":': 38, '<': 97, '<?xml version=': 0, '>': 102, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '25', 'dir': 'jupyter-structured-clean-dedup', 'id': '428569', 'license': 'MIT', 'n_tokens_mistral': 6763, 'n_tokens_neox': 6304, 'n_words': 2800, 'path': 'exercises/exercises-day1-ola.ipynb', 'repo_name': 'expertanalytics/course_exercises', 'size': '21045'}