Shanraq.org Shanraq.org

Python: from data to your own digest

Fifty-five lessons: from the first numbers to a digest that fetches open data by itself, counts it, and refreshes on a schedule. The course comes out lesson by lesson. Every lesson is open: free, no sign-up.

Lessons: 56 · 642 min to read · 1845 min to practise

Every lesson is open: reading needs no account.

Start with lesson one →

  1. A second free course: Python for data and AI

    The site's second free course — 55 lessons, from data to a digest that updates itself. Why Python, why data and AI rather than the web, and what the reader will actually count: prices up 3.48 times, and what cost a thousand tenge now costs 3481. No account, no payment, in three languages.

    9 min to read

  2. Counting for yourself: prices, the tenge and one question

    The first lesson of the Python course. No libraries have to be installed: a ready program fetches the official numbers itself. Prices in Kazakhstan are 3.48 times higher than in 2010: what cost a thousand tenge now costs 3481. And after the same shock inflation in other countries of the region is three times lower, which is the first reason to count for yourself.

    9 min to read · 35 min to practise

  3. A workplace: Python, the project's environment and the first run

    The second lesson of the Python course. We install the language, create the project's environment and move yesterday's program into it. Measured: a package installed into the environment is invisible outside it — `ModuleNotFoundError` — which is exactly what an environment is for. The `.venv` folder weighs 17 MB and never goes into the repository.

    12 min to read · 20 min to practise

  4. The first program: numbers, strings and output

    The third lesson of the Python course. We count on a shop receipt: two divisions instead of one, the remainder, a power, and then strings — trim, replace, cut, glue. And f-strings, which put a number inside text: 52,751,740,084,727 tenge with separators and 11.4% to one decimal place.

    11 min to read · 40 min to practise

  5. Variables and types: why "260" and 260 are different things

    The fourth lesson of the Python course. Data from somebody else's file arrives as text, and until the text becomes a number, adding it makes no sense: "260" + "260" gives 260260. The money rule is measured too: 0.1 + 0.2 is not 0.3, while Decimal is. Plus None, the truth of "0", and the difference between == and is.

    10 min to read · 40 min to practise

  6. A list and a tuple: a receipt that counts itself

    The fifth lesson of the Python course. Prices in a list: indexes, slices, changes, the sum and the maximum — without a single loop. The difference everybody trips over is measured: sorted() returns a new list, .sort() changes the original and returns None. And a tuple that refuses to change: TypeError.

    9 min to read · 40 min to practise

  7. A dictionary: a link instead of hoping the order holds

    The sixth lesson of the Python course. Prices by name: `get` instead of an error, `in` instead of a search, `items()` as a ready table. Measured: reaching for a missing key is a `KeyError` rather than emptiness; Counter tallies a basket in one line, and defaultdict supplies the zero where an ordinary dictionary fails.

    12 min to read · 40 min to practise

  8. Conditions: if, elif, else — and the number that is not there

    The seventh lesson of the Python course. A number on its own says nothing: is 11.4% a lot or a little? A condition compares, and the comparison picks the road. Measured on inflation: Kazakhstan at 11.4% in 2025, the world at 3.0%. Plus the first rule of working with data: a gap in a series is not a zero, and `is None` is how you ask.

    10 min to read · 40 min to practise

  9. Loops: for, range, and what zip does silently

    The eighth lesson of the Python course. One comparison instead of thirty: a loop walks the series itself, `continue` steps over the year with no data, an accumulator gives the average — 11.52% across five years. Plus two traps: `zip` silently cuts to the shorter series, and a `while` without a way out walks off the edge.

    12 min to read · 40 min to practise

  10. Functions: a name for a calculation, and an assert that catches a wrong number

    The ninth lesson of the Python course. The walk over a series gets a name and works on any series: Kazakhstan at 11.52%, the world at 4.68%. Plus an `assert` that stops the calculation on an empty series, the `lambda` in `sorted(key=)`, and a measured trap: `into=[]` in a header keeps its values between calls.

    13 min to read · 40 min to practise

  11. Errors and exceptions: no file, or rubbish inside it

    The tenth lesson of the Python course. Three of five rows from somebody else's export made it through: a comma is a full stop, while "no data" and an empty string are not numbers. `try/except/else/finally`, an error name of your own in one line, and the skill that matters — reading a traceback from its last line upwards.

    13 min to read · 40 min to practise

  12. Files and pathlib: a path that does not break on somebody else's machine

    The eleventh lesson of the Python course. The program writes its data, reads it line by line and puts the report beside it. The path is built by `Path` rather than by gluing strings: on Windows the same path is written with a backslash. And the encoding is always named — leave it out and the system chooses it for you.

    11 min to read · 40 min to practise

  13. CSV: a comma inside a field, and the mark Excel leaves

    The twelfth lesson of the Python course. On the row `2022,15.0,"shock, war, logistics"`, `split(",")` returns five pieces instead of three — measured. The csv module returns three. Plus `DictReader`, the required `newline=""`, and Excel's mark, which makes `row["year"]` answer `KeyError` while the column is right there.

    11 min to read · 40 min to practise

  14. JSON: the API answer we took on trust in the first lesson

    The thirteenth lesson of the Python course. The World Bank's answer is a list of two elements: a service part and the records. We take it apart by hand and find what spoils a calculation silently: `null` becomes `None`, and numeric keys come back from a round trip through JSON as strings — `2025` goes out, `"2025"` comes in.

    12 min to read · 40 min to practise

  15. Dates and time: 45 days, a timezone, and a format that reads two ways

    The fourteenth lesson of the Python course. There are 45 days between the 15th of January and the 1st of March, and `timedelta` counts them, not you. Plus two traps: `01.02.2026` parses under two formats without an error — as January and as February — and a naive time cannot be taken from an aware one, which Python says outright.

    13 min to read · 40 min to practise

  16. Sets: `&`, `-`, `|`, and what the second export does not have

    The fifteenth lesson of the Python course. Of the five names in the list only four are different: a set swallows the repeat without a word. In exchange, three questions about two exports take three signs: `&` for what is in both, `-` for what is gone, `|` for all of it. Plus `{}` is an empty dictionary, not an empty set.

    8 min to read · 40 min to practise

  17. Iterators and generators: 800,984 bytes against 208

    The sixteenth lesson of the Python course. A hundred thousand squares take almost four megabytes as a list and 376 bytes as a generator — measured, with every number counted in. The difference is not in how you count them but in when the values appear. Plus the mistake that matters: a generator is good for one pass, and the second one quietly gives you nothing.

    13 min to read · 40 min to practise

  18. A module and a package of your own: `import`, `__name__`, and the file that replaced a standard one

    The seventeenth lesson of the Python course. The digest has grown into one long file, and it is time to lay it out under names. `import` runs somebody else's file whole, `__name__` tells a module from the file that was started, and your own `csv.py` beside the program quietly replaces the standard one.

    12 min to read · 40 min to practise

  19. HTTP from Python: the status, the bytes, and two different failures

    The eighteenth lesson of the Python course. A request to the National Bank: 200, `text/xml`, 11,407 bytes — and those are `bytes`, not a string. Plus the difference that breaks error handling: `HTTPError` means the server said no, `URLError` means there was no answer at all.

    11 min to read · 40 min to practise

  20. Parsing XML: a tree instead of a string, `None` instead of a tag, and an address before the name

    The nineteenth lesson of the Python course. The same answer from the National Bank, read by a parser this time: 39 currencies, the rate taken by the name of a tag rather than by cutting a string. Plus two traps: `find` returns `None` for a tag that is not there, and a tag in a namespace is not found by its short name at all.

    11 min to read · 40 min to practise

  21. Politeness to somebody else's server: a timeout, a backoff and a cache

    The twentieth lesson of the Python course. Politeness to somebody else's server is three concrete things: a timeout, so you do not wait for ever; a retry with the pause doubling, so you do not hit what is already down; and a cache, so you do not ask twice. Measured on a server of our own: four readings, one request.

    11 min to read · 40 min to practise

  22. SQLite: a history instead of an overwritten file, a `?` instead of gluing

    The twenty-first lesson of the Python course. A database instead of a file: a row is added, yesterday's stay, and running the same day again makes no duplicates. Plus two measured traps: a value glued into a query returned the whole table instead of one currency, and closing without a `commit` lost the row.

    17 min to read · 40 min to practise

  23. SQL from Python: `GROUP BY`, `JOIN`, and an empty answer that is not zero

    The twenty-second lesson of the Python course. The database does the counting: `GROUP BY` gives one row per currency, `JOIN` brings the names from a second table, and a month is a group made of the first seven characters of the date. Plus the trap: over an empty answer `avg` returns `None` rather than zero.

    14 min to read · 40 min to practise

  24. On a schedule: arguments, a journal, a lock and an exit code

    The twenty-third lesson of the Python course. A program that works through the night without you: arguments instead of editing the source, a journal with levels instead of `print`, a lock against two runs at once, and the exit code — the one thing the scheduler actually reads. Plus cron, launchd and systemd.

    14 min to read · 40 min to practise

  25. Why pandas, when lists already work: what the measurement showed

    The twenty-fourth lesson of the Python course. The pandas module opens with the question it ought to open with: is the library worth it. A measurement answers, not belief. Where pandas is forty times faster, where it is merely twice, where it loses to an ordinary loop, what the memory costs — and the rule for when a table is needed.

    15 min to read · 40 min to practise

  26. The module's vocabulary: eight words a table speaks in

    Not a lesson but a reference page, standing in front of the heavy part of the pandas module. Eight words — a table, a column, a label and a position, a mask, a missing value, a key, a type and a shape — on one table of three rows. Read it once now and come back when a word turns up in a lesson.

    5 min to read

  27. DataFrame and Series: labels instead of numbers

    The twenty-fifth lesson of the Python course. A table whose rows carry labels does not answer the way a list does: `loc` takes by label, `iloc` by number, and two series are added by year rather than by position. `DataFrame` and `Series`, the index, slices whose ends differ, and the `NaN` that appears on its own.

    13 min to read · 40 min to practise

  28. Reading and writing: CSV, JSON and SQL in one line

    The twenty-sixth lesson of the Python course. Somebody else's file means a separator, a comma inside the decimals, a BOM and its own mark for a missing value; in a loop you have to remember every one of them, and in `read_csv` they are arguments. `read_csv`, `to_csv`, `read_sql` and `to_sql` — and what a file does not remember about a table.

    15 min to read · 40 min to practise

  29. Selecting and filtering: keeping only what is needed

    The twenty-seventh lesson of the Python course. A mask is a column of yes and no, and a whole table is filtered with one. `df[mask]`, `.loc[mask, columns]`, `&`, `|`, `~` and the brackets that are not decoration, `isin`, `between`, `str` — and the labels a filter leaves exactly as they were, which is where `loc` and `iloc` finally part company.

    11 min to read · 40 min to practise

  30. Grouping and aggregates: counting by category and by month

    The twenty-eighth lesson of the Python course. `groupby` is a loop written as a question: cut the table by a key, count each piece, put the pieces back. `agg` with column names of your own, two keys, a key the table does not hold, `transform` for a share, the difference between `count` and `size`, and the groups that go missing without a word.

    13 min to read · 40 min to practise

  31. Joining tables: gluing two sources on a key

    The twenty-ninth lesson of the Python course. `merge` joins two tables on a key — and by default throws away, in silence, every row that found no match. `inner` and `left`, `indicator`, keys with different names, `join` by label, and the duplicated key that multiplies rows and the sums along with them.

    14 min to read · 40 min to practise

  32. Dirty data: gaps, duplicates and types

    The thirtieth lesson of the Python course. Data arrives broken: numbers as text, a city spelled three ways, a duplicate row and an empty cell. `isna`, `to_numeric` with `errors="coerce"`, `astype`, `drop_duplicates` — and the question the lesson turns on: drop a gap or fill it, and what filling it costs.

    16 min to read · 40 min to practise

  33. Time in a table: resampling, rolling means and honesty

    The thirty-first lesson of the Python course. An index of dates can do what a list cannot: `asfreq` shows the days that are not there, `resample` folds days into weeks, `rolling` smooths. And the measured price of smoothing: the range halves, and the day of the real peak disappears from the series.

    9 min to read · 40 min to practise

  34. matplotlib: the first chart, and a file rather than a window

    The thirty-second lesson of the Python course. We draw the series from the last lesson: a sheet and axes, a line, labels, a grid, a legend — and save it to a file without a single window opening. Plus the three things that break a chart for everybody: `Agg`, the order of drawing before saving, and a figure nobody closed.

    13 min to read · 40 min to practise

  35. How not to lie with a chart: the scale, the zero and the labels

    The thirty-third lesson of the Python course. The same two numbers: from zero the bar is 1.31 times taller, with the axis starting at eight it is 4.86. The same segment of a line: 14 degrees on a wide sheet and 73 on a narrow one. Per cent against percentage points, the choice of window — and a chart that checks itself.

    15 min to read · 40 min to practise

  36. The report as a page: a table, a picture and a source in one file

    The thirty-fourth lesson of the Python course. HTML built out of data: a template instead of glued strings, `html.escape` for everything that came from the table, the picture inside the file — and a page that checks itself. One file that opens in any browser, prints to PDF and travels as a single attachment.

    13 min to read · 40 min to practise

  37. Numbers in a report: formats, rounding and units

    The thirty-fifth lesson of the Python course. `1234567.891` and `1 234 567,89` are one number and two different reports. Format strings, the local separators, the `round` that goes to the even one, `Decimal` for money, and the sum of rounded rows that does not equal the rounded sum.

    12 min to read · 40 min to practise

  38. A digest that updates itself: the whole pipeline

    The thirty-sixth lesson of the Python course and the end of the module on reports. Four steps in one `main()`, a write through a temporary file, a repeat with no consequences and an exit code; the exercise adds `--dry-run`. And the rule it was all for: a source that fell over leaves yesterday's report rather than erasing it.

    14 min to read · 40 min to practise

  39. The mean, the median and the spread: when the mean lies

    Lesson thirty-seven of the Python course, and the start of the module about money. Ten countries, one year: the mean is 20.48 %, the median 13.88 % — and the mean is higher than what eight of the ten actually had. `mean`, `median`, `std`, the quarters, and the rule that picks a measure before anyone has seen the result.

    8 min to read · 40 min to practise

  40. Inflation as a multiplier: what a tenge was worth

    Lesson thirty-eight of the Python course. Add up ten yearly rates and you get 92.98 % — and that is the wrong answer: prices grew by 141.98 %, because percentages multiply. The index through `cumprod`, the multiplier, the average yearly rate, and a thousand tenge of 2014 with 413 of them left by 2024.

    14 min to read · 40 min to practise

  41. Your own inflation: your basket against the official one

    The thirty-ninth lesson of the Python course. "Inflation is 8 %, but everything doubled for me" — both sentences can be true at once, and what tells them apart is weights. The basket, each item's share of the spending, its contribution to the growth — and why a simple mean over items promises 84.6 % where the basket grew by 69.8 %.

    14 min to read · 20 min to practise

  42. Where money comes from: cash, deposits, credit

    The fortieth lesson of the Python course. Of all the money in the country the National Bank printed nine per cent; the other ninety-one are records in bank accounts that appeared at the moment a bank made a loan. The aggregates as a matryoshka, money per tenge of GDP, the money multiplier — and what the law says about all of it.

    18 min to read · 20 min to practise

  43. How to read official statistics

    The forty-first lesson of the Python course. One row of an official table carries five different numbers about the same prices, and all five are right. Which question each of them answers, how to recover the weights of the official basket that the release does not print, and why the World Bank ends up with a different figure than the news.

    19 min to read · 20 min to practise

  44. What comes from outside and what is ours: one shock, many prices

    The forty-second lesson of the Python course. In 2022 world food rose by 14.9 % — the same for everybody. Inflation among ten neighbours came out between 8.6 and 72.3 %, a difference of eight times. The two usual culprits, the rate and money, are tested with a link — and one country moves the answer, while for money it flips the sign.

    16 min to read · 20 min to practise

  45. The first model: linear regression

    The forty-third lesson of the Python course. A line drawn through sixteen years of prices gives an R² of 0.927 and loses to the rule "same as last year" — by a hundredth of a point. Its error is not random but has a shape: plus, minus, plus. A line in logarithms is wrong by half as much, because prices multiply.

    15 min to read · 20 min to practise

  46. Training and checking: why a model cannot mark its own work

    The forty-fourth lesson of the Python course. The same model on the same data: an error of 2.48 points on the years it was taught, and 35.99 on the years it never saw. A random cut instead of a cut by time shows 5.37 and lies. A fifth-degree polynomial learns almost perfectly and is three times worse than a line.

    9 min to read · 20 min to practise

  47. Classification and the confusion matrix

    The forty-fifth lesson of the Python course. The model answers yes or no: will next year's inflation be above ten per cent. Accuracy of 0.75 against 0.675 for a model that always says no — and the confusion matrix shows that twenty of the twenty-six high years were missed along the way.

    16 min to read · 20 min to practise

  48. Where a model ends

    The forty-sixth lesson of the Python course and the end of the module on models. The same line promises an index of 146,206 for the year 2100 and minus a thousand for 1800 — and marks neither answer as doubtful. The border has to be written by hand: what it learned on, where it was checked, past which year it no longer holds.

    10 min to read · 20 min to practise

  49. A language model on your own computer: Ollama and the first local request

    Lesson forty-seven of the Python course. Install Ollama, get a first answer without an API key, and follow the whole request: the program sends JSON to localhost, checks the HTTP response, and only then takes the model's text. The data stays on your computer unless you expose the local port yourself.

    8 min to read · 30 min to practise

  50. A prompt as code: parameters and reproducibility

    Lesson forty-eight of the Python course. Stop composing a model request as one disposable line: separate role, task, data, and constraints; pin temperature, seed, and an output limit; then store the whole request beside its result. An identical request becomes a repeatable experiment, not a promise of byte-for-byte identical prose.

    7 min to read · 30 min to practise

  51. A structured response: model JSON and validation

    Lesson forty-nine of the Python course. Ask the model for JSON governed by a schema instead of free-form prose, parse it with the standard `json` module, and validate it with `jsonschema`. Three separate gates—syntax, structure, and facts—keep a plausible-looking error out of the report.

    6 min to read · 30 min to practise

  52. A model on our data: verify every number

    Lesson fifty of the Python course. Give the model a small table with stable source identifiers, accept structured claims, and compare every number with its source row. The model proposes wording; our code inserts the verified value, unit, and year into the final text.

    6 min to read · 30 min to practise

  53. What a model cannot be trusted with: fact, inference, cause

    Lesson fifty-one of the Python course. Separate an observation, a computed result, and a causal explanation. Code can verify a value and reproduce a calculation, but plausible causal prose remains a hypothesis until it has independent evidence and human review.

    6 min to read · 30 min to practise

  54. pytest tests: verify the pipeline's promises

    Lesson fifty-two of the Python course. Turn project rules into automated tests: check the normal case, boundaries, and expected failures, then run the whole suite with one command.

    4 min to read · 10 min to practise

  55. Type annotations and mypy: fail before running

    Lesson fifty-three of the Python course. Describe data shapes in signatures, narrow `None` with a check, and run `mypy` so incompatible values are found before the program executes.

    4 min to read · 10 min to practise

  56. Dependencies: reproducible beats “works on my machine”

    Lesson fifty-four of the Python course. Separate direct dependencies from a complete lock file, pin the Python environment, and rebuild it cleanly so the project can still run a year from now.

    2 min to read · 10 min to practise

  57. Finale: verify, publish, and reproduce

    Lesson fifty-five and the end of the introductory Python course. Combine checks into one gate, build an immutable report directory, publish only after success, and expose verifiable source and build information to the reader.

    4 min to read · 10 min to practise