From 1d371049e85aec28daa45a9f7e4bd444453a5027 Mon Sep 17 00:00:00 2001 From: bitmole Date: Tue, 13 Feb 2024 10:07:30 -0700 Subject: [PATCH 1/8] Start --- .python-version | 1 + grep | 0 pyproject.toml | 27 +++++++++++++++++++++++++++ requirements-dev.lock | 10 ++++++++++ requirements.lock | 10 ++++++++++ src/python_mastery/1_1/art.py | 15 +++++++++++++++ src/python_mastery/__init__.py | 2 ++ 7 files changed, 65 insertions(+) create mode 100644 .python-version create mode 100644 grep create mode 100644 pyproject.toml create mode 100644 requirements-dev.lock create mode 100644 requirements.lock create mode 100644 src/python_mastery/1_1/art.py create mode 100644 src/python_mastery/__init__.py diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..171a6a93 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12.1 diff --git a/grep b/grep new file mode 100644 index 00000000..e69de29b diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..eebf2823 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "python-mastery" +version = "0.1.0" +description = "Advanced python coursework." +authors = [ + { name = "bitmole", email = "robert@idea-loop.com" } +] +dependencies = [] +readme = "README.md" +requires-python = ">= 3.8" + +[project.scripts] +hello = "python_mastery:hello" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.rye] +managed = true +dev-dependencies = [] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.build.targets.wheel] +packages = ["src/python_mastery"] diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 00000000..8f230963 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,10 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: false +# with-sources: false + +-e file:. diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 00000000..8f230963 --- /dev/null +++ b/requirements.lock @@ -0,0 +1,10 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: false +# with-sources: false + +-e file:. diff --git a/src/python_mastery/1_1/art.py b/src/python_mastery/1_1/art.py new file mode 100644 index 00000000..dc9d9c13 --- /dev/null +++ b/src/python_mastery/1_1/art.py @@ -0,0 +1,15 @@ +# art.py + +import sys +import random + +chars = '\|/' + +def draw(rows, columns): + for r in range(rows): + print(''.join(random.choice(chars) for _ in range(columns))) + +if __name__ == "__main__": + if len(sys.argv) != 3: + raise SystemExit('Usage: art.py rows columns') + draw(int(sys.argv[1]), int(sys.argv[2])) diff --git a/src/python_mastery/__init__.py b/src/python_mastery/__init__.py new file mode 100644 index 00000000..555cc6f8 --- /dev/null +++ b/src/python_mastery/__init__.py @@ -0,0 +1,2 @@ +def hello(): + return "Hello from python-mastery!" From 4d60fb3a68a421bc8b016cdd061b552cc92c344c Mon Sep 17 00:00:00 2001 From: bitmole Date: Tue, 13 Feb 2024 11:10:55 -0700 Subject: [PATCH 2/8] Portfolio cost from file. --- src/python_mastery/1_3/pcost.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/python_mastery/1_3/pcost.py diff --git a/src/python_mastery/1_3/pcost.py b/src/python_mastery/1_3/pcost.py new file mode 100644 index 00000000..af9aa16d --- /dev/null +++ b/src/python_mastery/1_3/pcost.py @@ -0,0 +1,17 @@ +# pcost.py + +import sys + +def total_cost(path): + stockdicts = [] + with open(path) as f: + for row in f: + stockdicts.append( + dict(zip(['name', 'shares', 'price'], row.split()))) + return sum(int(s['shares'])*float(s['price']) for s in stockdicts) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + raise SystemExit('Usage: pcost portfile') + print('Total cost:', total_cost(sys.argv[1])) From b017ff33ffbb17e3ada570c96f63101d2500de65 Mon Sep 17 00:00:00 2001 From: bitmole Date: Wed, 14 Feb 2024 09:29:50 -0700 Subject: [PATCH 3/8] Parsing error handling --- src/python_mastery/1_3/pcost.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/python_mastery/1_3/pcost.py b/src/python_mastery/1_3/pcost.py index af9aa16d..19dd80ba 100644 --- a/src/python_mastery/1_3/pcost.py +++ b/src/python_mastery/1_3/pcost.py @@ -2,16 +2,25 @@ import sys -def total_cost(path): +def portfolio_cost(path): stockdicts = [] with open(path) as f: for row in f: - stockdicts.append( - dict(zip(['name', 'shares', 'price'], row.split()))) - return sum(int(s['shares'])*float(s['price']) for s in stockdicts) + name, shares, price = row.split() + try: + shares = int(shares) + price = float(price) + d = dict(zip(['name', 'shares', 'price'], (name, shares, price))) + print(d) + stockdicts.append(d) + except ValueError as e: + print('Could not parse: ', row, end='') + print('Reason: ', e) + + return sum(s['shares'] * s['price'] for s in stockdicts) if __name__ == "__main__": if len(sys.argv) != 2: raise SystemExit('Usage: pcost portfile') - print('Total cost:', total_cost(sys.argv[1])) + print('Total cost:', portfolio_cost(sys.argv[1])) From 5e9da0d4e0f44bb455dc76c51c57e157eb735b88 Mon Sep 17 00:00:00 2001 From: bitmole Date: Wed, 14 Feb 2024 09:44:00 -0700 Subject: [PATCH 4/8] Reorg. --- src/python_mastery/{1_1 => }/art.py | 0 src/python_mastery/{1_3 => }/pcost.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/python_mastery/{1_1 => }/art.py (100%) rename src/python_mastery/{1_3 => }/pcost.py (100%) diff --git a/src/python_mastery/1_1/art.py b/src/python_mastery/art.py similarity index 100% rename from src/python_mastery/1_1/art.py rename to src/python_mastery/art.py diff --git a/src/python_mastery/1_3/pcost.py b/src/python_mastery/pcost.py similarity index 100% rename from src/python_mastery/1_3/pcost.py rename to src/python_mastery/pcost.py From 763312146cc14b360a399a49995a66c267ea90b9 Mon Sep 17 00:00:00 2001 From: bitmole Date: Wed, 14 Feb 2024 10:21:55 -0700 Subject: [PATCH 5/8] Portfolio cost script --- pyproject.toml | 1 + src/python_mastery/__init__.py | 4 ++++ src/python_mastery/stock.py | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 src/python_mastery/stock.py diff --git a/pyproject.toml b/pyproject.toml index eebf2823..590cdaf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ requires-python = ">= 3.8" [project.scripts] hello = "python_mastery:hello" +pcost = "python_mastery:pcost" [build-system] requires = ["hatchling"] diff --git a/src/python_mastery/__init__.py b/src/python_mastery/__init__.py index 555cc6f8..a2ba189e 100644 --- a/src/python_mastery/__init__.py +++ b/src/python_mastery/__init__.py @@ -1,2 +1,6 @@ def hello(): return "Hello from python-mastery!" + +def pcost(): + from .pcost import portfolio_cost + print('Total: ', portfolio_cost('Data/portfolio.dat')) diff --git a/src/python_mastery/stock.py b/src/python_mastery/stock.py new file mode 100644 index 00000000..b1a4341a --- /dev/null +++ b/src/python_mastery/stock.py @@ -0,0 +1,11 @@ +# stock.py + +class Stock(object): + """Represents a stock""" + def __init__(self, name, shares, price): + self._name = name + self._shares = shares + self._price = price + + def cost(self): + return self._shares * self._price From 513116bbe633e2c9d37f8d8bb51305490a361068 Mon Sep 17 00:00:00 2001 From: bitmole Date: Tue, 27 Feb 2024 11:32:20 -0700 Subject: [PATCH 6/8] Memory usage of different data structures. --- src/python_mastery/readrides.py | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/python_mastery/readrides.py diff --git a/src/python_mastery/readrides.py b/src/python_mastery/readrides.py new file mode 100644 index 00000000..3788b994 --- /dev/null +++ b/src/python_mastery/readrides.py @@ -0,0 +1,122 @@ +# readrides.py + +import csv +from collections import namedtuple + +class Ride(object): + def __init__(self, route, date, daytype, rides): + self._route = route + self._date = date + self._daytype = daytype + self._rides = rides + +class SlotRide(object): + __slots__ = ['_route', '_date', '_daytype', '_rides'] + def __init__(self, route, date, daytype, rides): + self._route = route + self._date = date + self._daytype = daytype + self._rides = rides + +TupRide = namedtuple('TupRide', ['route', 'date', 'daytype', 'rides']) + + +def read_rides_as_strings(filename): + """ + Read the bus ride data as a list of strings + """ + records = [] + with open(filename) as f: + for row in f: + records.append(row) + return records + +def read_rides_as_tuples(filename): + """ + Read the bus ride data as a list of tuples + """ + records = [] + with open(filename) as f: + rows = csv.reader(f) + headings = next(rows) # skip headers + for row in rows: + route, date, daytype, rides = row + rides = int(rides) + records.append((route, date, daytype, rides)) + return records + +def read_rides_as_namedtuples(filename): + """ + Read the bus ride data as a list of named tuples + """ + records = [] + with open(filename) as f: + rows = csv.reader(f) + headings = next(rows) # skip headers + for row in rows: + route, date, daytype, rides = row + rides = int(rides) + records.append(TupRide(route, date, daytype, rides)) + return records + +def read_rides_as_dicts(filename): + """ + Read the bus ride data as a list of dictionaries + """ + records = [] + with open(filename) as f: + rows = csv.reader(f) + headings = next(rows) # skip headers + for row in rows: + route, date, daytype, rides = row + record = dict( + zip( + ('route', 'date', 'daytype', 'rides'), + (route, date, daytype, int(rides)))) + records.append(record) + return records + +def read_rides_as_objects(filename): + """ + Read the bus ride data as a list of objects + """ + records = [] + with open(filename) as f: + rows = csv.reader(f) + headings = next(rows) # skip headers + for row in rows: + route, date, daytype, rides = row + records.append(Ride(route, date, daytype, int(rides))) + return records + +def read_rides_as_slobjects(filename): + """ + Read the bus ride data as a list of objects with slots + """ + records = [] + with open(filename) as f: + rows = csv.reader(f) + headings = next(rows) # skip headers + for row in rows: + route, date, daytype, rides = row + records.append(SlotRide(route, date, daytype, int(rides))) + return records + +if __name__ == "__main__": + import sys + import tracemalloc + if len(sys.argv) != 3: + raise SystemExit('usage: readrides filename datastructure[strings, tuples, namedtupes, dicts, objects, slots]') + + callmap = { + "strings": read_rides_as_strings, + "tuples": read_rides_as_tuples, + "namedtuples": read_rides_as_namedtuples, + "dicts": read_rides_as_dicts, + "obs": read_rides_as_objects, + "slobs": read_rides_as_slobjects, + } + + tracemalloc.start() + rows = callmap[sys.argv[2]](sys.argv[1]) + print('Memory Use: Current %d, Peak %d' % tracemalloc.get_traced_memory()) From a0435c233fbcf089e01ec4ee3efc3fa17736ef1e Mon Sep 17 00:00:00 2001 From: bitmole Date: Tue, 27 Feb 2024 16:05:53 -0700 Subject: [PATCH 7/8] Clarify main --- src/python_mastery/readrides.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python_mastery/readrides.py b/src/python_mastery/readrides.py index 3788b994..f4a2b556 100644 --- a/src/python_mastery/readrides.py +++ b/src/python_mastery/readrides.py @@ -117,6 +117,7 @@ def read_rides_as_slobjects(filename): "slobs": read_rides_as_slobjects, } + reader, filename = callmap[sys.argv[2]], sys.argv[1] tracemalloc.start() - rows = callmap[sys.argv[2]](sys.argv[1]) + rows = reader(filename) print('Memory Use: Current %d, Peak %d' % tracemalloc.get_traced_memory()) From 2fdfdd65e7e9d1a771844e9b7ef547a640d3f9bf Mon Sep 17 00:00:00 2001 From: bitmole Date: Wed, 28 Feb 2024 20:13:51 -0700 Subject: [PATCH 8/8] Collections, data analysis practice. --- src/python_mastery/readport.py | 23 +++++++++++++++++++++++ src/python_mastery/readrides.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/python_mastery/readport.py diff --git a/src/python_mastery/readport.py b/src/python_mastery/readport.py new file mode 100644 index 00000000..6ca985a3 --- /dev/null +++ b/src/python_mastery/readport.py @@ -0,0 +1,23 @@ +# readport.py + +import csv + +def read_portfolio(filename): + """Reads a file into a list of dicts representing stock holdings + + :filename: TODO + :returns: TODO + + """ + portfolio = [] + with open(filename) as f: + rows = csv.reader(f) + headers = next(rows) # skip headers + for row in rows: + holding = { + 'name': row[0], + 'shares': int(row[1]), + 'price': float(row[2]), + } + portfolio.append(holding) + return portfolio diff --git a/src/python_mastery/readrides.py b/src/python_mastery/readrides.py index f4a2b556..01ff1440 100644 --- a/src/python_mastery/readrides.py +++ b/src/python_mastery/readrides.py @@ -102,6 +102,32 @@ def read_rides_as_slobjects(filename): records.append(SlotRide(route, date, daytype, int(rides))) return records +def anal(dicts): + + # how many bus routes exist in Chicago + routes = {d['route'] for d in dicts} + print('Number of bus routes: ', len(routes)) + + # how many people rode the number 22 bus on February 2, 2011? + rides = [d['rides'] for d in dicts if d['route']=='22' and d['date']=='02/02/2011'] + print('Riders on a given day: ', sum(rides)) + + # what's the total number of rides taken on each bus route? + from collections import Counter + totals = Counter() + for d in dicts: + totals[d['route']] += d['rides'] + print('Total rides by route: ', totals.most_common(5)) + + # what five bus routes had the greatest ten-year increase in ridershiop from 2001 to 2011? + old = {d['route']: d['rides'] for d in dicts if d['date'].endswith('2001')} + new = {d['route']: d['rides'] for d in dicts if d['date'].endswith('2011')} + increases = Counter() + for route, rides in new.items(): + increases[route] += rides - old.get(route, 0) + print('Top five increases: ', increases.most_common(5)) + + if __name__ == "__main__": import sys import tracemalloc @@ -119,5 +145,8 @@ def read_rides_as_slobjects(filename): reader, filename = callmap[sys.argv[2]], sys.argv[1] tracemalloc.start() - rows = reader(filename) + recs = reader(filename) print('Memory Use: Current %d, Peak %d' % tracemalloc.get_traced_memory()) + + if sys.argv[2] == 'dicts': + anal(recs)