import%20marimo%0A%0A__generated_with%20%3D%20%220.15.2%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20duckdb%20as%20ddb%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20return%20mo%2C%20pl%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20plotly.express%20as%20px%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20return%20go%2C%20np%2C%20px%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20raw_df%20%3D%20mo.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20SELECT%20*%20FROM%20read_csv(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22umweltbingo.txt%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20delim%3D'%5Ct'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20header%3Dtrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20auto_detect%3Dtrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20null_padding%3Dtrue%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(raw_df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20Wir%20arbeiten%20mit%20Zahlen%20f%C3%BCr%20die%20Klassen%3A%20B%20%3D%200%2C%20I%20%3D%201%2C%20N%20%3D%202%20usw.%20Damit%20l%C3%A4sst%20sich%20die%20Klasse%20einer%20Zahl%20einfach%20errechnen.%0A%0A%20%20%20%20Man%20sieht%20hier%20schon%2C%20dass%20die%20Zahlen%20resp.%20Klassen%20immer%20aufsteigend%20gespeichert%20sind.%0A%20%20%20%20Evtl.%20werden%20die%20Zahlen%20vor%20dem%20Speichern%20immer%20sortiert%2C%20evtl.%20h%C3%A4ngt%20das%20aber%20auch%20damit%20zusammen%20wie%20sie%20generiert%20werden%20(man%20sieht%20sp%C3%A4ter%20auch%20Verteilungsunterschiede%20zwischen%20niedrigen%20und%20hohen%20Klassen).%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(pl%2C%20raw_df)%3A%0A%20%20%20%20%23%20Start%20with%20your%20raw_df%0A%20%20%20%20clean_df%20%3D%20(%0A%20%20%20%20%20%20%20%20raw_df%0A%20%20%20%20%20%20%20%20%23%201.%20Create%20proper%20date%20column%0A%20%20%20%20%20%20%20%20.with_columns(%0A%20%20%20%20%20%20%20%20%20%20%20%20pl.date(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20year%3Dpl.col(%22Jahr%22)%2C%20month%3Dpl.col(%22Monat%22)%2C%20day%3Dpl.col(%22Tag%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20).alias(%22Datum%22)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%23%202.%20Collect%20Zahl1..Zahl22%20into%20a%20list%0A%20%20%20%20%20%20%20%20.with_columns(%0A%20%20%20%20%20%20%20%20%20%20%20%20pl.concat_list(%5Bpl.col(f%22Zahl%7Bi%7D%22)%20for%20i%20in%20range(1%2C%2023)%5D).alias(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Zahlen%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%23%203.%20Collect%20Serien%2FLos%20pairs%20into%20a%20struct%20list%0A%20%20%20%20%20%20%20%20.with_columns(%0A%20%20%20%20%20%20%20%20%20%20%20%20pl.concat_list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pl.struct(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pl.col(f%22Serien-Nr.%7Bi%7D%22).alias(%22Serien-Nr%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pl.col(f%22Los-Nr.%7Bi%7D%22).alias(%22Los-Nr%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20i%20in%20range(1%2C%206)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20).alias(%22Lose%22)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%23%204.%20Drop%20old%20columns%20if%20you%20don%E2%80%99t%20need%20them%0A%20%20%20%20%20%20%20%20.drop(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%22Tag%22%2C%20%22Monat%22%2C%20%22Jahr%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%5Bf%22Zahl%7Bi%7D%22%20for%20i%20in%20range(1%2C%2023)%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%5Bf%22Serien-Nr.%7Bi%7D%22%20for%20i%20in%20range(1%2C%206)%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%5Bf%22Los-Nr.%7Bi%7D%22%20for%20i%20in%20range(1%2C%206)%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20df%20%3D%20clean_df.with_columns(%0A%20%20%20%20%20%20%20%20pl.col(%22Zahlen%22)%0A%20%20%20%20%20%20%20%20.list.eval(((pl.element()%20-%201)%20%2F%2F%2015).alias(%22Klassen%22))%0A%20%20%20%20%20%20%20%20.alias(%22Klassen%22)%0A%20%20%20%20)%0A%20%20%20%20df%0A%20%20%20%20return%20(df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20Wir%20bestimmen%20zu%20jeder%20Klasse%20wie%20oft%20sie%20an%20einem%20gegebenen%20Tag%20vorkommt%20und%20bezeichnen%20das%20als%20Klassenanzahl.%0A%20%20%20%20Man%20sieht%20hier%20bereits%2C%20dass%20Klassenanzahl%201%20tats%C3%A4chlich%20relativ%20h%C3%A4ufig%20vorkommt.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df%2C%20pl)%3A%0A%20%20%20%20counts_without_zeros%20%3D%20(%0A%20%20%20%20%20%20%20%20df.explode(%22Klassen%22)%0A%20%20%20%20%20%20%20%20.group_by(%22Datum%22%2C%20%22Klassen%22)%0A%20%20%20%20%20%20%20%20.len()%0A%20%20%20%20%20%20%20%20.sort(%22len%22)%0A%20%20%20%20%20%20%20%20.rename(dict(len%3D%22Klassenanzahl%22))%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Fill%20in%20missing%20zeros%0A%20%20%20%20all_dates%20%3D%20df.select(pl.col(%22Datum%22).unique())%0A%20%20%20%20all_klassen%20%3D%20df.select(pl.col(%22Klassen%22).explode().unique())%0A%20%20%20%20full_grid%20%3D%20all_dates.join(all_klassen%2C%20how%3D%22cross%22)%0A%0A%20%20%20%20counts%20%3D%20(%0A%20%20%20%20%20%20%20%20full_grid.join(counts_without_zeros%2C%20on%3D%5B%22Datum%22%2C%20%22Klassen%22%5D%2C%20how%3D%22left%22)%0A%20%20%20%20%20%20%20%20.fill_null(0)%0A%20%20%20%20%20%20%20%20.sort(%5B%22Datum%22%2C%20%22Klassen%22%5D)%0A%20%20%20%20)%0A%0A%20%20%20%20counts%0A%20%20%20%20return%20(counts%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22Die%20Verteilungen%20der%20Klassenanzahlen%20scheinen%20f%C3%BCr%20jede%20Klasse%20ungef%C3%A4hr%20gleich%20zu%20sein.%20Bei%20der%20letzten%20Klasse%20(%60O%60%20bzw.%20%605%60)%20gibt%20es%20weniger%20merkliche%20Unterschiede%2C%20aber%20nichts%20*zu*%20drastisches.%20Man%20sieht%20auch%20hier%2C%20dass%20eine%20Klassenanzahl%20von%201%20auff%C3%A4llig%20h%C3%A4ufig%20auftritt.%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(counts%2C%20go%2C%20pl)%3A%0A%20%20%20%20_pdf%20%3D%20(%0A%20%20%20%20%20%20%20%20counts.group_by(%22Klassen%22%2C%20%22Klassenanzahl%22)%0A%20%20%20%20%20%20%20%20.agg(pl.col(%22Datum%22).len().alias(%22Anzahl%22))%0A%20%20%20%20%20%20%20%20.sort(%22Klassen%22%2C%20%22Klassenanzahl%22)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Pivot%20the%20data%20to%20create%20a%20matrix%0A%20%20%20%20pivot_df%20%3D%20_pdf.pivot(values%3D%22Anzahl%22%2C%20index%3D%22Klassen%22%2C%20on%3D%22Klassenanzahl%22)%0A%0A%20%20%20%20%23%20Convert%20to%20numpy%20array%0A%20%20%20%20matrix%20%3D%20pivot_df.select(pl.exclude(%22Klassen%22)).to_numpy()%0A%0A%20%20%20%20%23%20Get%20the%20axis%20labels%0A%20%20%20%20klassen_labels%20%3D%20list(%22BINGO%22)%20%20%23%20pivot_df%5B%22Klassen%22%5D.to_list()%0A%20%20%20%20klassenanzahl_labels%20%3D%20pivot_df.columns%5B1%3A%5D%20%20%23%20Skip%20the%20%22Klassen%22%20column%0A%0A%20%20%20%20%23%20Create%20the%20Plotly%20heatmap%0A%20%20%20%20fig%20%3D%20go.Figure(%0A%20%20%20%20%20%20%20%20data%3Dgo.Heatmap(%0A%20%20%20%20%20%20%20%20%20%20%20%20z%3Dmatrix%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dklassenanzahl_labels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dklassen_labels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20colorscale%3D%22Viridis%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20colorbar%3Ddict(title%3D%22Anzahl%22)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Update%20layout%0A%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Heatmap%20wie%20viele%20Tage%20eine%20Kombination%20aus%20Klasse%20und%20Klassenanzahl%20aufweisen%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Klassenanzahl%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Klassen%22%2C%0A%20%20%20%20%20%20%20%20width%3D1000%2C%0A%20%20%20%20%20%20%20%20height%3D600%2C%0A%20%20%20%20)%0A%0A%20%20%20%20fig.show()%0A%20%20%20%20return%20(matrix%2C)%0A%0A%0A%40app.cell%0Adef%20_(matrix%2C%20pl)%3A%0A%20%20%20%20pl.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Klassenanzahl%22%3A%20range(8)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Wahrscheinlichkeit%22%3A%20matrix.sum(axis%3D0)%20%2F%20matrix.sum()%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Simulation%0A%0A%20%20%20%20Jetzt%20simulieren%20wir%20das%20ganze%20nochmal%20mit%20Rejection%20Sampling.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20np)%3A%0A%20%20%20%20NUM_BALLS%20%3D%2075%0A%20%20%20%20NUM_CLASSES%20%3D%205%0A%20%20%20%20DRAW_SIZE%20%3D%2022%0A%20%20%20%20NUM_MAX_DRAWS_PER_CLASS%20%3D%207%0A%0A%0A%20%20%20%20def%20simulate_draw(rng%3A%20np.random.BitGenerator)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Simulate%20a%20single%20draw%20of%2022%20out%20of%2075%20numbers%20without%20replacement%2C%0A%20%20%20%20%20%20%20%20not%20yet%20taking%20into%20account%20the%20restriction%20that%20there%20mustn't%20be%20no%20more%20than%207%20numbers%20per%20class%20in%20a%20draw.%0A%0A%20%20%20%20%20%20%20%20Returns%20an%20array%20encoding%20how%20many%20of%20the%20numbers%20fall%20into%20each%20class.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20drawn%20%3D%20rng.choice(NUM_BALLS%2C%20size%3DDRAW_SIZE%2C%20replace%3DFalse)%0A%20%20%20%20%20%20%20%20classes%20%3D%20drawn%20%2F%2F%20(NUM_BALLS%20%2F%2F%20NUM_CLASSES)%0A%20%20%20%20%20%20%20%20return%20np.bincount(classes%2C%20minlength%3DNUM_CLASSES)%0A%0A%0A%20%20%20%20def%20mc_sim(num_samples%3A%20int)%3A%0A%20%20%20%20%20%20%20%20seed%20%3D%200%0A%20%20%20%20%20%20%20%20rng%20%3D%20np.random.default_rng(seed)%0A%20%20%20%20%20%20%20%20samples%20%3D%20np.zeros((num_samples%2C%20NUM_CLASSES)%2C%20dtype%3Dnp.uint8)%0A%20%20%20%20%20%20%20%20for%20i%20in%20mo.status.progress_bar(%0A%20%20%20%20%20%20%20%20%20%20%20%20range(num_samples)%2C%20title%3D%22Running%20Simulation%22%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20samples%5Bi%2C%20%3A%5D%20%3D%20simulate_draw(rng)%0A%20%20%20%20%20%20%20%20is_valid%20%3D%20(samples%20%3C%3D%20NUM_MAX_DRAWS_PER_CLASS).all(axis%3D1)%0A%20%20%20%20%20%20%20%20num_valid%20%3D%20sum(is_valid)%0A%20%20%20%20%20%20%20%20return%20count_occurences(samples%5B%3A%2C%20%3A%5D)%2C%20count_occurences(%0A%20%20%20%20%20%20%20%20%20%20%20%20samples%5Bis_valid%2C%20%3A%5D%0A%20%20%20%20%20%20%20%20)%0A%0A%0A%20%20%20%20def%20count_occurences(draws)%3A%0A%20%20%20%20%20%20%20%20arrs%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20col_idx%20in%20range(NUM_CLASSES)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20all_counts%20%3D%20draws%5B%3A%2C%20col_idx%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20arrs.append(np.bincount(all_counts%2C%20minlength%3DNUM_MAX_DRAWS_PER_CLASS))%0A%0A%20%20%20%20%20%20%20%20max_len%20%3D%20max(len(arr)%20for%20arr%20in%20arrs)%0A%20%20%20%20%20%20%20%20padded_arrs%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20np.pad(arr%2C%20(0%2C%20max_len%20-%20len(arr))%2C%20%22constant%22)%20for%20arr%20in%20arrs%0A%20%20%20%20%20%20%20%20%5D%0A%0A%20%20%20%20%20%20%20%20return%20np.array(padded_arrs)%0A%20%20%20%20return%20(mc_sim%2C)%0A%0A%0A%40app.cell%0Adef%20_(mc_sim)%3A%0A%20%20%20%20NUM_SAMPLES%20%3D%20500_000%0A%20%20%20%20all_counts%2C%20valid_counts%20%3D%20mc_sim(NUM_SAMPLES)%0A%20%20%20%20return%20all_counts%2C%20valid_counts%0A%0A%0A%40app.cell%0Adef%20_(all_counts%2C%20px)%3A%0A%20%20%20%20px.imshow(all_counts%2C%20title%3D%22Alle%20Ziehungen%20(ohne%20max%20%3D%207%20Einschr%C3%A4nkung)%22%2C%20y%3Dlist(%22BINGO%22)%2C%20labels%3Ddict(x%3D%22Klassenanzahl%22%2C%20y%3D%22Klasse%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(px%2C%20valid_counts)%3A%0A%20%20%20%20px.imshow(valid_counts%2C%20title%3D%22Valide%20Ziehungen%20(mit%20max%20%3D%207%20Einschr%C3%A4nkung)%22%2C%20y%3Dlist(%22BINGO%22)%2C%20labels%3Ddict(x%3D%22Klassenanzahl%22%2C%20y%3D%22Klasse%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22Man%20sieht%2C%20dass%20die%20Wahrscheinlichkeiten%20weit%20unter%20denen%20der%20tats%C3%A4chlichen%20Daten%20bleiben.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(all_counts%2C%20np%2C%20pl%2C%20valid_counts)%3A%0A%20%20%20%20pl.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Klassenanzahl%22%3A%20range(all_counts.shape%5B1%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Wahrscheinlichkeit%20alle%22%3A%20all_counts.sum(axis%3D0)%20%2F%20all_counts.sum()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Wahrscheinlichkeit%20valide%22%3A%20np.pad(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20valid_counts.sum(axis%3D0)%20%2F%20valid_counts.sum()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(0%2C%20all_counts.shape%5B1%5D%20-%20valid_counts.shape%5B1%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22constant%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
d7aab212b0b6fdac412830c0116a953457f8b8dfc4148aaa66bf3de38bda8142