Sign in

michael 📈 👨‍💻

@mikedecr.computer
269 followers 295 following 403 posts

Low-latency quant finance R&D. Former political scientist, sometimes bike rider. Long form mikedecr.computer

PostsRepliesMedia
michael 📈 👨‍💻 @mikedecr.computer · 08/10/2026
the couchy distribution
714810
michael 📈 👨‍💻 @mikedecr.computer · 21/08/2026
every inconvenience is an opportunity for evil
Python code:

import polars as pl


class Columns:
    def __getattr__(self, name) -> pl.Expr:
        if name.startswith("_"):
            raise AttributeError(name)
        return pl.col(name)

    def __getitem__(self, name) -> pl.Expr:
        return pl.col(name)


c = Columns()

df = pl.DataFrame({"foo": [1, 2, 3], "bar": [4, 5, 6]})

df.with_columns(baz=c.foo + c.bar)

# shape: (3, 3)
# ┌─────┬─────┬─────┐
# │ foo ┆ bar ┆ baz │
# │ --- ┆ --- ┆ --- │
# │ i64 ┆ i64 ┆ i64 │
# ╞═════╪═════╪═════╡
# │ 1   ┆ 4   ┆ 5   │
# │ 2   ┆ 5   ┆ 7   │
# │ 3   ┆ 6   ┆ 9   │
# └─────┴─────┴─────┘
111
michael 📈 👨‍💻 @mikedecr.computer · 01/06/2026
I like language exercises like these, so I implemented some of these functions in Hy, which is a Lisp that generates Python. We like functions right? Would we still like functions if they were **evil**? mikedecr.computer/blog/r-hy/
hy code and output: 

(import toolz [first second])
(require hyrule [->])

(defn which [lst]
  (-> lst
    (enumerate)
    ((curry filter (compose bool second)))
    ((curry map first))
    (list)))

(which [False False True False True])

[2, 4]
bugs bunny guns meme: forgive me lord but it's time to go back to the old me
000
michael 📈 👨‍💻 @mikedecr.computer · 20/05/2026
scope in Python *is* possible but for your sanity you should ask no more questions
Python console input and output:

>>> with Bind(1) as x:
...     print(x)
...     
1
>>> x
Traceback (most recent call last):
  File "<python-input-12>", line 1, in <module>
    x
NameError: name 'x' is not defined
000
michael 📈 👨‍💻 @mikedecr.computer · 29/04/2026
I love the conceptual model of ggplot but believe that implementing the exact syntax feel in python is not desirable or important. The "adding" of the plot components... how much will you suffer for this? I write this helper fn all the time
python code snip:

from pick_your_ggplot_emulation_pkg import *


def fnplot(data, *args):
    return sum(args, start=ggplot(data))


fnplot(
    data,
    aes(x="x", y="y"),
    geom_point(),
    geom_line()
)


# vs...
(
    ggplot(data) +
    aes(x="x", y="y") +
    geom_point() +
    geom_line()
)


# or...
ggplot(data) + \
    aes(x="x", y="y") + \
    geom_point() + \
    geom_line()
030
michael 📈 👨‍💻 @mikedecr.computer · 19/01/2026
I don't normally do really oop-y solutions to advent of code problems but it helped clean up the main fns a lot here github.com/mikedecr/aoc...
Screenshot of python code:

@dataclass
class CircuitNetwork:
    _circuits: list[Circuit]

    # idk if this is the best place for this method but oh well.
    def contains_link(self, a: Box, b: Box) -> bool:
        """True if a local circuit contains boxes A and B"""
        return any(a in circ and b in circ for circ in self._circuits)

    def attach(self, a: Circuit, b: Circuit) -> None:
        """Consolidates circuits A and B into a single circuit"""
        assert a != b, "Circuits must be distinct"
        assert a in self._circuits, f"Circuit {a=} is not in the network"
        assert b in self._circuits, f"Circuit {b=} is not in the network"
        self._circuits.remove(a)
        self._circuits.remove(b)
        self._circuits.append(a.union(b))

    def get_circuit(self, pt: Box) -> Circuit:
        """Returns the circuit containing provided Box"""
        for circ in self._circuits:
            if pt in circ:
                return circ
010
michael 📈 👨‍💻 @mikedecr.computer · 10/01/2026
I suppose this is also less clunky then popping an element only to maybe re-append it
def attach_interval(lst: list[Interval], new: Interval) -> list[Interval]:
    """Collapse interval `new` to the last element of `lst` if possible, otherwise append."""
    if len(lst) == 0:
        lst.append(new)
        return lst
    if (collapsed := maybe_collapse_intervals(lst[-1], new)):
        lst[-1] = collapsed
    else:
        lst.append(new)
    return lst
000
michael 📈 👨‍💻 @mikedecr.computer · 10/01/2026
de-structuring the tuples here creates a more pleasant reading experience
python code:

def maybe_collapse_intervals(left: Interval, right: Interval) -> tuple[Interval, ...] | None:
    """Return collapsed interval if possible, or None"""
    a, b = left
    x, y = right
    if a <= x <= y <= b:    # left contains right
        return (a, b)
    elif x <= a <= b <= y:  # right contains left
        return (x, y)
    elif a <= x <= b <= y:  # left straddles right lower bound
        return (a, y)
    elif x <= a <= y <= b:  # left straddles right upper bound
        return (x, b)
    else:
        return
100
michael 📈 👨‍💻 @mikedecr.computer · 10/01/2026
did I overly functionalize Day 5.2 or is this just the thing to do
Python code:

@app.command()
def day_5_2(test: Flag("--test") = False):
    data_path: Path = Data(5, test=test)
    intervals_lst: list[Interval] = []
    with open(data_path, "r") as f:
        for line in f:
            if line == "\n":
                break
            interval: Interval = tuple(eval(chr) for chr in line.strip().split("-"))
            intervals_lst.append(interval)
    # with the intervals sorted, we can do this w/ reduce
    # fn: combine RIGHT w/ tail of LEFT or append RIGHT to LEFT
    intervals_lst.sort()
    collapsed_intervals: list[Interval] = reduce(attach_interval, intervals_lst, [])
    result: int = sum(1 + (right - left) for left, right in collapsed_intervals)
    print("solution:", result)

def attach_interval(lst: list[Interval], new: Interval) -> list[Interval]:
    """Collapse interval `new` to the last element of `lst` if possible, otherwise append."""
    if len(lst) == 0:
        lst.append(new)
        return lst
    end = lst.pop(-1)
    if (collapsed := maybe_collapse_intervals(end, new)):
        lst.append(collapsed)
    else:
        lst.append(end)
        lst.append(new)
    return lst

def maybe_collapse_intervals(left: Interval, right: Interval) -> tuple[Interval, ...] | None:
    """Return collapsed interval if possible, or None"""
    if left[0] <= right[0] <= right[1] <= left[1]:  # left contains right
        return left
    elif right[0] <= left[0] <= left[1] <= right[1]:  # right contains left
        return right
    elif left[0] <= right[0] <= left[1] <= right[1]:  # left straddles right lower bound
        return (left[0], right[1])
    elif right[0] <= left[0] <= right[1] <= left[1]:  # left straddles right upper bound
        return (right[0], left[1])
    else:
        returnterminal input and output:

$ time uv run aoc day-5-2 
solution: 357907198933892
uv run aoc day-5-2  0.08s user 0.02s system 94% cpu 0.110 total
100
michael 📈 👨‍💻 @mikedecr.computer · 27/09/2025
weird idea: map in terms of reduce
screenshot of python code:

# this is the classical definition of map
def map(fn, xs: list):
    if len(xs) == 0:
        return []
    else:
        return [fn(xs[0]), *map(fn, xs[1:])]


# but you can implement as a reduce
from functools import reduce

def map(fn, xs: list):
    return reduce(_as_binary_op(fn), xs, list())

# casting func into a binary operation
def _as_binary_op(fn):
    def binary_op(accum: list, x):
        accum.append(fn(x))
        return accum
    return binary_op


map(str.upper, ["a", "b", "c"])
# ['A', 'B', 'C']
240
michael 📈 👨‍💻 @mikedecr.computer · 21/09/2025
Look, I ride a bike and I want better bike infrastructure, but this is magical thinking. If every car disappeared there would be way more bikes, way more bikers who don't ride as fast as you want to go, so way more bike traffic
030
michael 📈 👨‍💻 @mikedecr.computer · 21/09/2025
will anybody ever want this? idk. a cheaper / better way might be to just define the fns with lambdas
text in the R console: 

R > by_sex = \(x) group_by(x, sex, .add = TRUE)
R > mean_mass = \(x) summarize(x, mass = mean(body_mass_g))
R > penguins |> by_sex() |> mean_mass()
# A tibble: 3 × 2
  sex     mass
  <fct>  <dbl>
1 female 3862.
2 male   4546.
3 NA       NA
000
michael 📈 👨‍💻 @mikedecr.computer · 21/09/2025
I re-published this post about "lazy" pipeline functions: mikedecr.computer/blog/lazy-dp... Basically, exploring how to make pipeline-style R code more re-usable and composable by abusing partial function application
Screenshot from the text of the blog post:

More abstractly, what I want is more separation of functions from data in the R pipeline style.
In some situation, I want to pass data `x` to functions `f`, `g`, and `h`.
But instead of writing this:

```{r}
#| eval: false
x |>
    f(...) |>
    g(...) |>
    h(...)
```

I want to write something like this:

```{r}
#| eval: false
# first argument omitted from each of these expressions
pipeline = compose(f(...), g(...), h(...))

# supply data to the ultimate function call
x |> pipeline()
```


`pipeline` is a function that captures unevaluated function calls on `f`, `g`, and `h` with their first arguments omitted.
Then I pass data `x` such that the data pass through the first argument of each function in the chain.

Why do I want this?
Because it should be easier to build a pipeline of expressions that can be re-used on different datasets.
I am thinking specically of examples like this:

```{r}
#| eval: false
# I could evaluate this function as-is
x |> pipeline()

# and I could also drop in extra steps j and k
x |> j() |> pipeline()
x |> k() |> pipeline()
```
130
michael 📈 👨‍💻 @mikedecr.computer · 14/09/2025
I wrote a "lazy pipe" operator %=>% that lets you build function compositions from partial function expressions in a linear way. Instead of passing data up front, you define pipeline steps without reference to the data. You get reusable + composable pipeline steps w/out as much function boilerplate
Screenshot of R code that reads:

library(dplyr)
library(palmerpenguins)

# how a normal pipeline looks
mass_stats <- penguins |>
    group_by(species, island) |>
    summarize(
        mean_body_mass = mean(body_mass_g, na.rm = TRUE),
        sd_body_mass = sd(body_mass_g, na.rm = TRUE),
    ) |>
    print()


# using lazy pipe
# create a function by composing partial expressions with %=>% operator
compute_mass_stats = 
    group_by(species, island, .add = TRUE) %=>%
    summarize(
        mean_body_mass = mean(body_mass_g, na.rm = TRUE),
        sd_body_mass = sd(body_mass_g, na.rm = TRUE)
    )

# then pass data
compute_mass_stats(penguins)
Screenshot of R code that reads:

# by using .add = TRUE we can prepend extra groupings to our pipeline
# so we can re-use code more efficiency when we need to slice data more ways
by_sex = group_by(sex, .add = TRUE) |> as_partial()

penguins |> 
    by_sex() |>
    compute_mass_stats()
020
michael 📈 👨‍💻 @mikedecr.computer · 01/09/2025
good slide from this talk: www.youtube.com/watch?v=EGLo...
slide from a presentation that reads "Syntax don't solve your problems"
110
michael 📈 👨‍💻 @mikedecr.computer · 14/06/2025
i'm chewing on this fly.io/blog/youre-a...
Screenshot of text from the linked blog post. The text reads:

but the craft

Do you like fine Japanese woodworking? All hand tools and sashimono joinery? Me too. Do it on your own time.

† (I’m a piker compared to my woodworking friends)

I have a basic wood shop in my basement †. I could get a lot of satisfaction from building a table. And, if that table is a workbench or a grill table, sure, I’ll build it. But if I need, like, a table? For people to sit at? In my office? I buy a fucking table.

Professional software developers are in the business of solving practical problems for people with code. We are not, in our day jobs, artisans. Steve Jobs was wrong: we do not need to carve the unseen feet in the sculpture. Nobody cares if the logic board traces are pleasingly routed. If anything we build endures, it won’t be because the codebase was beautiful.

Besides, that’s not really what happens. If you’re taking time carefully golfing functions down into graceful, fluent, minimal functional expressions, alarm bells should ring. You’re yak-shaving. The real work has depleted your focus. You’re not building: you’re self-soothing.
110
michael 📈 👨‍💻 @mikedecr.computer · 11/05/2025
at a high level it's basically this
screen shot from the blog post. Text reads:

I am being silly, but I have this irrational beef with Python. Python has a lot of trivial syntax sugar for operations that could simply be functions. I like functions because you can do things with functions: composing, mapping, reducing, currying. You can’t do much with syntax sugar. In Python, all the sugar is parsed away to C code before any operations are actually executed against your data. So the syntax sugar in Python is convenient, but programmatically it is kind of useless.
000
michael 📈 👨‍💻 @mikedecr.computer · 19/04/2025
...which will let me figure out why the checking for legal moves is broken. The existing code lets you just bring a piece out of bounds to the left, then when the piece settles it is somehow chopped between left and right sides of the board
A gif showing a tetronimo piece going out of bounds to the left of the game board, and when the piece is sent to the bottom of the board, some of it appears on both sides of the board.
100
michael 📈 👨‍💻 @mikedecr.computer · 19/04/2025
- I had to add a few missing pieces? Originally S and Z were somehow blended into one piece, so had to be separated. We had an L but not a J? O needed to be added entirely. - I am now running main() using typer, which lets me easily pass CLI args. This is very convenient for activating "debug" mode
A Git diff showing the addition of `import typer; typer.run(main)`a git diff showing the addition of a debug argument to the main() function. The argument is used to pass a 0 fall speed to the TimeUpdate class if debug is True, else we pass a fall speed of 50.
100
michael 📈 👨‍💻 @mikedecr.computer · 19/04/2025
The game display looks like this. I have already fixed a few things about it: - colors were random which was confusing. I didn't expand it to full colors but did add some classics: I=blue, O=gray, T=yellow - pieces were rotating counter-clockwise-only so that had to get fixed
 a computer window showing a tetris-like game. pieces are red, green, blue, yellow, and red. Some pieces have already been played.
100
michael 📈 👨‍💻 @mikedecr.computer · 29/03/2025
not my cat, not my balcony
030
michael 📈 👨‍💻 @mikedecr.computer · 12/03/2025
if you can remap keys with either firmware or software I put the EQ here on key tap, and ctrl/cmd on key hold, so it’s on the thumb
Picture of a keyboard. The left GUI / Cmd key is circled
110
michael 📈 👨‍💻 @mikedecr.computer · 26/01/2025
i let myself get nerd-sniped by this and I looked up the marginal rates and computed the weighted avg tax rate for some income blah blah
a plot of marginal and effective tax rate as income goes from $0 to $1mil. the effective tax rate is always below the marginal tax rate and is much smoother than the marginal rate
220
michael 📈 👨‍💻 @mikedecr.computer · 20/01/2025
I want a way to search / interact with my archive of tweet data so I began a little project for parsing & presenting the data. extremely WIP, no designs are finalized, just kinda goofing around for starters github.com/mikedecr/twe...
screenshot of a terminal command and output:

uv run tmd parse --archive ../twitter-archive/twits -o tweets.json && head -20 tweets.json

[
  {
    "tweet": {
      "edit_info": {
        "initial": {
          "editTweetIds": [
            "1855701514406309953"
          ],
          "editableUntil": "2024-11-10T20:58:12.000Z",
          "editsRemaining": "5",
          "isEditEligible": true
        }
      },
      "retweeted": false,
      "source": "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>",
      "entities": {
        "hashtags": [],
        "symbols": [],
        "user_mentions": [],
        "urls": []
000
michael 📈 👨‍💻 @mikedecr.computer · 18/01/2025
it’s Saturday
photo of apartment living room, v60 coffee making station on the counter in the foreground
020
michael 📈 👨‍💻 @mikedecr.computer · 03/12/2024
I saw some people talking about "No syntactically significant whitespace Python" which was enticingly evil... I am being too strict about it...function defs aren't even allowed, only lambdas, so the type hints are trying to make it interpretable but now it look messy. Some of my worst filth yet!
"""
No-Significant-Whitespace Python.
Not even function defs!
"""

import itertools
from pathlib import Path
from typing import Any, List, Callable as Function


parse_words: Function[str, List[str]] = lambda chars: \
    [s for s in chars.split(" ") if s != ""]

parse_int_sequence: Function[str, List[int]] = lambda chars: \
    [int(word) for word in parse_words(chars)]

diff_series: Function[List[int], List[int]] = lambda series: \
    [a - b for a, b in itertools.pairwise(series)]

all_same_sign: Function[List[int], bool] = lambda seq: \
    all(x < 0 for x in seq) or all(x > 0 for x in seq)

# haters weep that we find a use for this yet again...
juxt: Function[List[Function], Function[Any, List[Any]]] = lambda fns: \
    lambda *args, **kws: [fn(*args, **kws) for fn in fns]

good_seq_checks: Function[List[int], List[bool]] = lambda seq: \
    juxt([all_same_sign, lambda lst: all(1 <= abs(x) <= 3 for x in lst)])(diff_series(seq))

is_safe_sequence: Function[List[int], bool] = lambda seq: \
    all(good_seq_checks(seq))

part_one: Function[Path, int] = lambda filepath: \
    sum(is_safe_sequence(parse_int_sequence(seq))
        for seq in open(filepath).read().splitlines())


if __name__ == "__main__":

    pt2_data = Path("data/2024/02")
    test_file = pt2_data / "test.txt"
    final_file = pt2_data / "final.txt"

    assert part_one(test_file) == 2
    print(f"{part_one(final_file)=}")
010
michael 📈 👨‍💻 @mikedecr.computer · 15/11/2024
Test post from BlueSky python client bsky-bridge
Screen grab of the Python code that created this post.
160
michael 📈 👨‍💻 @mikedecr.computer · 09/11/2024
"the terminal is a vibe" but playing with personal website themes. Quarto is great but I do not like the Bootswatch themes for websites and hacking on CSS is always miserable for me. Remember the ol' Hugo days? I'm trying to make quarto x hugo go nicely. Theme is github.com/panr/hugo-th...
Rendered web page, dark screen, monospace text, orange accent color, very retro terminal looking.

Text is rendered latex from following code:

Dollars $ a = x $ and parens \(b + c\)


Double dollar:
$$
a = x
$$

Square bracket:
\[
a + 1
\]

Escaping dollars between \\$100 and \\$200.
010
michael 📈 👨‍💻 @mikedecr.computer · 09/11/2024
the terminal is a vibe, what else is there to say
neovim home screen with a huge ascii art banner reading YOU'RE IN MIKE'S WORLD
000
michael 📈 👨‍💻 @mikedecr.computer · 09/11/2024
progressive academics went insane about (a) Silver not using comma-robust or (b) accusing him of finding excuses to invent bad news for Democrats (?) instead of, idk entertaining the face validity that inflation might be a big part of a future story of Dem loss. Lefty derangement about NS is so odd
270
michael 📈 👨‍💻 @mikedecr.computer · 07/02/2024
011
michael 📈 👨‍💻 @mikedecr.computer · 30/12/2023
julia is a delight. an in-your-face type system is good, folks
Julia code:

# the clever thing about this is we put Joker and J in the same enum :)
@enum Face Joker Two Three Four Five Six Seven Eight Nine T J Q K A
@enum HandType HighCard OnePair TwoPair ThreeOfAKind FullHouse FourOfAKind FiveOfAKind
000
michael 📈 👨‍💻 @mikedecr.computer · 28/12/2023
quick recursive take on AOC day 9
Carbon screenshot of Julia code.
Code is too long for alt text, find the code here: https://raw.githubusercontent.com/mikedecr/advent_of_code_2023/main/jl/09-both.jl
110
michael 📈 👨‍💻 @mikedecr.computer · 06/12/2023
😇
julia code:

    # walk down the line, look for matches
    first_match = find_first_pattern(line, needles)
    # walk "up" the reversed line for reversed matches, reverse the detected match
    last_match = let
        # taking advantage of let block to contain clutter
        each_needle_reversed = reverse.(string.(needles))
        each_line_reversed = reverse(line)
        reversed_last_match = find_first_pattern(each_line_reversed, each_needle_reversed)
        reverse(reversed_last_match)
    end
000
michael 📈 👨‍💻 @mikedecr.computer · 06/12/2023
030
michael 📈 👨‍💻 @mikedecr.computer · 08/11/2023
oh weird, sorting the counting by the average of the two points makes what initially looks like a suspicious vertical symmetry in the data and then you're like wait no obviously the average is between the two nearly-equally-weighted points 😵‍💫
010
michael 📈 👨‍💻 @mikedecr.computer · 31/10/2023
new silliness in my already very silly {prefix} R package
R REPL input and output: 

[r] ▶ devtools::load_all()
ℹ Loading prefix

[r] ▶ mult(2, 5)
[1] 10

[r] ▶ mult(2)
function(...) operator(a, ...)
<environment: 0x11ff4d000>

[r] ▶ dbl = mult(2)

[r] ▶ dbl(5)
[1] 10
150
michael 📈 👨‍💻 @mikedecr.computer · 18/10/2023
the hardest thing IMO about writing functions atop dplyr in R is R's evaluation model, and I am not very smart. Julia just gives me :symbols directly so the lispy playfulness is way more on display. The flexibility w/ operators and function syntax make it easy to write what I want how I want it
Julia code: 

using PalmerPenguins
using DataFrames, DataFramesMeta
penguins = DataFrame(PalmerPenguins.load())

# this feels so easy
partial(fn::Function, a...) = (b...) -> fn(a..., b...)
rpartial(fn::Function, b...) = (a...) -> fn(a..., b...)

# this feels so easy
lazyverb(fn::Function) = (b...) -> df::DataFrame -> fn(df, b...)
subsetting = lazyverb(subset)

# that lisp feel
eq = ==

adelies = subsetting(:species => eq("Adelie") |> ByRow)
adelies(penguins)

# layers of functional expressiveness
keep_species = sp::String -> subsetting(:species => eq(sp) |> ByRow)

chinstraps = keep_species("Chinstrap")
chinstraps(penguins)
010
michael 📈 👨‍💻 @mikedecr.computer · 25/07/2023
But the code I'm playing with in the top-linked post lets you do that. More examples in the excerpt:
Here’s the recipe I stated above:

filter to Adelie species
compute a ratio of bill length to depth
find the mean of this ratio

Here we write these steps with delayed verbs. Each of these expressions return separate functions from dataframe to dataframe.

adelie_only = dfilter(species == "Adelie")

compute_length_to_depth = dmutate(
    bill_length_to_depth = bill_length_mm / bill_depth_mm
)

mean_bill_ratio = dsummarize(
    avg_bill_ratio = mean(bill_length_to_depth, na.rm = TRUE)
)

I can now write a “data pipeline” not as eager actions on data, but as functions that don’t need to know anything about data.

# using "right" / "postfix" composition
adelie_mean_bill_ratio = (
    adelie_only %;%
    compute_length_to_depth %;%
    mean_bill_ratio
)

Since my pipeline is a function, it is simple to apply it to any dataset I want. For instance, here it is in the full dataset:

adelie_mean_bill_ratio(penguins)
## # A tibble: 1 × 1
##   avg_bill_ratio
##            <dbl>
## 1           2
000