Was sagt mir ein Covid-Schnelltest Ergebnis?

written by Martin HĂ€cker on

Inspiriert von diesem Video von 3Blue1Brown, hat sich mein VerstĂ€ndnis wie man ĂŒber Covid-Tests nachdenkt, grundsĂ€tzlich gewandelt.

Zwar war mir schon sehr lange Bewusst, dass Test-Ergebnisse kontra-intuitiv sein können, weil sie eben auch Gesunde als krank erkennen können.

Das ist einfach nachzuvollziehen, wenn man einen Covid-Schnelltest anschaut. Die GĂŒte dieser Tests wird fast immer mit zwei Zahlen angegeben: SensitivitĂ€t (wie viele der tatsĂ€chlich Kranken werden erkannt) und SpezifizitĂ€t (wie viele der Gesunden werden auch als Gesund erkannt). Nehmen wir an unser Test hat jetzt 90% SensitivitĂ€t und 90% SpezifitĂ€t (fiktive Zahlen fĂŒr leichteres Rechnen - echte Zahlen kommen spĂ€ter).

Wende ich diesen Test jetzt auf 100 menschen in einem Land an, das Zero-Covid hat. Dann sollten trotzdem 10 Personen als Krank gemeldet werden. Was heißt das jetzt? Logischerweise nix, denn es sind ja alle Gesund.

Gehe ich stattdessen ins Krankenhaus auf die Covid-Station und mache den Test dort mit 100 Personen, dann sollten trotzdem 10 Personen als Gesund gemeldet werden. Was heißt das jetzt? Logischerweise auch nix, die Leute sind ja schon wegen Covid in Behandlung und nicht durch den Test plötzlich gesundet.

Und das ist die wichtige Denk-Änderung die 3Blue1Brown in seinem Video motiviert - man muss beim nachdenken ĂŒber Testergebnisse einfach immer auch darĂŒber nachdenken wie wahrscheinlich es ist das man tatsĂ€chlich Krank ist. (Bayes lĂ€ĂŸt grĂŒĂŸen)

Und jetzt kommt der Dreh - alles wird viel einfacher, wenn man von vorne herein nicht denkt dass der Test mir sagt ob ich gesund oder Krank bin, sondern:

Jeder Test hat ein Vorhersagekraft, und dieser Faktor verÀndert meine Wahrscheinlichkeit Krank zu sein.

Es wÀre also viel Einfacher, wenn man bei Tests nicht SensitivitÀt und SpezifizitÀt angibt, sondern daraus die Vorhersagekraft des Tests berechnet. Damit kann man dann nÀmlich plötzlich viel einfacher Denken und verstehen. Und insbesondere ist es viel einfacher eine 'Vorhersagekraft' nicht mit 'Wahrscheinlichkeit das ich Krank bin' zu verwechseln, was einfach jeder tut der sich mit dem Thema nicht intensiv auseinander setzt.

Und das beste: Wenn man nicht in Prozent, sondern in VerhÀltnissen rechnet, dann ist das sogar prÀzise!

Wie kommt man jetzt auf diese Vorhersagekraft (auch Bayes Faktor genannt)

Jeder Test ist durch zwei Zahlen (SensitivitÀt[welcher Anteil der Kranken wird erkannt], SpezifitÀt[welcher Anteil der Gesunden wird als Gesund erkannt]) bzw. durch die Komplemente davon (Falsch-Negativ-Rate, Falsch-Positiv-Rate) bestimmt.

Interessiert man sich jetzt fĂŒr die Frage was ein Positiver-Test aussagt, kann man aus dem Quotient von SensitivitĂ€t geteilt durch Falsch-Positiv-Rate die Vorhersagekraft eines Positiven Tests und aus dem Quotient von Falsch-Negativ-Rate geteilt durch SpezifitĂ€t die Vorhersagekraft eines Negativen Tests erhalten.

Ein Beispiel: Angenommen ein Test hat SensitivitĂ€t 90% und SpezifitĂ€t 95% (also Falsch-Negativ-Rate 10%, Falsch-Positiv-Rate 5%). Dann ist die Vorhersagekraft eines Positiven Tests 90%/5%=18. Das heißt, wenn ich einen Positiven Test habe, dann weiß ich, dass sich die Wahrscheinlichkeit das ich Krank bin 18 mal vergrĂ¶ĂŸert hat zu dem wie sie vorher war. Und umgekehrt: Ein Negativer Test 10%/95%≈  1/10. In Worten: Ein Negativer Test verkleinert meine Chance krank zu sein um etwa eine GrĂ¶ĂŸenordnung.

đŸ€Ż

Rechnet man in Prozent ist das leider nur eine AbschÀtzung, aber wenn man das ganze in Chancen) rechnet wird aus der AbschÀtzung eine prÀzise Formel!

đŸ€Ż đŸ€Ż

Schauen wir also mal auf ein paar reale Zahlen an.

In Berlin (stand 13. Mai 21, sind nach Pavels Covid Tabelle) derzeit einer von 394 Personen Ansteckend.

FĂŒr einen Covid-Test den ich gerade da habe findet sich hier eine SensitivitĂ€t ~93,5% und SpezifitĂ€t ~98%.

Die Vorhersagekraft eines Positiven Tests ist 93,5%/2% ~47. Demnach wĂ€chst meine Chance heute ansteckend zu sein von 1/394 um das fast fĂŒnfzigfache auf etwas mehr als 1/4.

Die Vorhersagekraft eines negativen Tests ist 6,5%/98%~0,06. Demnach wĂ€chst meine Chance Gesund zu sein um fast zwei GrĂ¶ĂŸenordnungen von 1/394 auf etwa 6,5/38.612.

Oh wie schön wĂ€re es, wenn das auch ĂŒberall so kommuniziert wĂŒrde


Open Source Flugelektronik 2 - BLE und Serial

written by Martin HĂ€cker on

Langsam nĂ€here ich mich meinen Soft-RF-GerĂ€ten an. Ich kann jetzt auch ĂŒber Bluetooth Low Energy (BLE) mit dem GerĂ€t reden. Das Python Framework Bleak war dabei unglaublich hilfreich, da ich fast den ganzen BLE-Code sehr plattform-neutral Scheiben kann. I.e. der Code ist zwar nur auf MacOS getestet, wird aber sehr wahrscheinlich auch unter Linux funktionieren. Der Code zu diesem Post liegt auf github.

Wie lÀuft das jetzt? Zuerst braucht man einen BLE-Scanner, um die Adresse des GerÀts zu finden. Bei mir sieht das so aus:

% ./ble_scanner.py
........................................
[...]
0975FAB7-6F50-4B60-A0D8-9F11C817FB6D: SoftRF-f0c010-LE
[...]

Mit dieser Adresse kann man dann reden um sich anzuschauen welche BLE-Services sie anbietet:

% ./ble_explore_device.py 0975FAB7-6F50-4B60-A0D8-9F11C817FB6D
Connected: True
[Service] 0000ffe0-0000-1000-8000-00805f9b34fb (Handle: 40): Vendor specific
    [Characteristic] 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read,write-without-response,notify), Value: b',,,,,,,,,99.99,99.99'
        [Descriptor] 00002901-0000-1000-8000-00805f9b34fb (Handle: 43): Characteristic User Description) | Value: b'HMSoft'
        [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 44): Client Characteristic Configuration) | Value: b'\x00\x00'

Was heißt das? Das gerĂ€t bietet den Service 0000ffe0-* an und darauf eine Characteristik 0000ffe1-* die wiederum zwei Deskriptoren enthĂ€lt. 00002901-*und 00002902-*. Nicht das ich BLE vollstĂ€ndig verstehe, aber zumindest weiß ich das ein Service der GrundsĂ€tzliche BehĂ€lter fĂŒr alle BLE Interaktionen ist und eine Charactreristik einen lese und schreibbaren Wert darstellt. Deskriptoren können darin verschachtelt sein und sind entweder zusĂ€tzliche lese und schreibbare Werte oder haben einen technischen Sinn. In diesem Beispiel ist der zweite Descriptor notwendig um auf der Charakteristik nicht nur lesen und schreiben, sondern auch Notify zu implementieren -> und das braucht es fĂŒr die Serielle Schnittstelle.

Entscheidend ist hier die Service-Charakteristik 0000ffe1-0000-1000-8000-00805f9b34fb Hier spielt die ganze Musik. Die Serielle Ausgabe des SoftRF kann man mittels Notify davon auslesen und man kann auf die Serielle Schnittstelle schreiben indem man darauf schreibt. Soweit so simpel. Und damit kriegt man auch schon ein sehr primitives UART hin:

% ./ble_uart.py 0975FAB7-6F50-4B60-A0D8-9F11C817FB6D
Connected, start typing and press ENTER...
0.00,0000.0000,N,00000.0000,E,0,00,100.0,0.0,M,0.0,M,,*5C
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$PFLAU,0,0,0,1,0,,0,,,*4F
$GPRMC,,V,,,,,,,,,,N*53
$GPGGA,000000.00,0000.0000,N,00000.0000,E,0,00,100.0,0.0,M,0.0,M,,*5C
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99$PFLAU,0,0,0,1,0,,0,,,*4F
$GPRMC,,V,,,,,,,,,,N*53
$GPGGA,000000.00,0000.0000,N,00000.0000,E,0,00,100.0,0.0,M,0.0,M,,*5C
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$PFLAU,0,0,0,1,0,,0,,,*4F
$GPRMC,,V,,,,,,,,,,N*53

Yay!

NĂ€chste Tasks: das NMEA Encoding verstehen und mal schauen ob ich damit eine erste simple Visualisierung hinbekomme.

Open Source Flugelektronik mit Lora

written by Martin HĂ€cker on

Inzwischen gibt es mehrere Projekte die Lora Funktechnik fĂŒr Ad-Hoc-Netzwerke in der Luft (und am Boden) verwenden.

SoftRF ist vermutlich das bekannteste, da es mit gĂŒnstiger Hardware eigentlich alle Standards unterstĂŒtzt. Dieses fĂŒr in der Luft und und dieses als GegengerĂ€t am Boden und fĂŒr Experimente - jeweils in der in Europa zugelassenen 868Mhz Variante - habe ich mir besorgt, da sie nur jeweils ~25€ Kosten.

SoftRF kommt darauf sogar schon vorinstalliert, das ist wirklich sehr komfortabel.

In Betrieb lassen sich die GerĂ€te auch ganz gut nehmen, einfach via USB an den Rechner anschließen, bei dem einen geht dann ein WLAN auf ĂŒber das man es Konfigurieren kann, bei dem anderen hat man direkten Zugriff ĂŒber die serielle:

$ pip install pyserial
$ python -m serial.tools.list_ports
[...]
$ python -m serial.tools.miniterm /path/to/port

Konfigurieren kann man das GerĂ€t dann ĂŒber eine Config-String, den man in diesem Web-Interface generieren kann.

Easy peasy.

Next Tasks:

  • NMEA Nachrichten Parsen und lesbar Ausgeben
  • Das zweite GerĂ€t ĂŒber Bluetooth Low Energy verbinden
  • BLE mit einem meiner Flug-Computer-Smartphone Apps verbinden - FlySkyHy oder eVario
  • Herausfinden was es braucht um da noch einen guten Barometrischen Sensor mit einzubauen

Wie und wann den Retter werfen?

written by Martin HĂ€cker on

Diesen Vortrag von Theo de Blic finde ich sehr sehenswert, da er a) viel Erfahrung mit Rettern hat - immerhin 14 RetterwĂŒrfe hat er schon durch, und b) einen wunderschön strukturierten Gedanken-Ablauf hat wan und wie man seine Retter werfen soll.

Besonders Wertvoll fand ich die Folie 'Know your limits' in denen er wiedergibt wann Er seinen Retter wirft. Ich habe beschlossen das ich mir diese 'Gedankenkarte' auf jeden fall auch ziehen möchte. Also: 'Note to self' - Wann immer eine dieser Situationen auftritt, egal welche Höhe, ziehe ich meine Rettung!

Know your limits

Höhe

  • Ich bin niedrig
  • Ich bin nicht sicher ob ich hoch oder niedrig bin

Verloren FĂŒhlen

  • Ich weiß nicht was ich tun soll
  • Ich weiß nicht wo ich bin
  • Ich verstehe nicht was passiert

Unlösbare Situationen

  • Line over
  • Mehrere twists
  • Große Krawatte
  • Kaputter FlĂŒgel

Crosswords Puzzle

written by Martin HĂ€cker on

KreuzwortrĂ€tsel finde ich eigentlich langweilig. Aber die Idee die Vorgaben ĂŒber RegulĂ€re AusdrĂŒcke zu machen finde ich klasse.

Hier geht es zum Puzzle - mit Herzlichem Dank an Jimb Esser.

UrsprĂŒnglich entworfen hat es Dan Gulotta am MIT aber die JS Implementierung von Jimb Esser ist einfach Gold wert.

Thermik-Gradienten lesen

written by Martin HĂ€cker on

Im aktuellen Thermik-Magazin gab es einen Artikel ĂŒber Thermik-Gradienten - und insbesondere diese kleine Tabelle, die es leicht macht aus der StĂ€rke der Gradienten auf die vorhandene Thermik zu schließen. Note to self: Gradienten ĂŒber 0,7 vorerst vermeiden.

Gradienten Legende

Dependable API Evolution

written by Martin HĂ€cker on

This is an old text that I just found on my hard disk. It's too good not to publish I think, as pretty much everything in it is still completely relevant. So here goes - a shout to the past, as this was written ca. 2014. At the time I discovered Semantic Versioning - and was discontent, because it is simply not enough. We as a community need a better name for what we want to do with APIs - just Semantic Versioning is not enough to reliably move forward and stay sane.

My Suggestion: Define Dependable API Evolution

The problem that prompted me to write this: I am sick and tired of APIs that do not evolve and that have old warts that are not fixed. At the same time I hate APIs that make every update to multi hour 'joy-ride' of debugging and finding out why the heck my system doesn't work anymore.

For this I think that Semantic Versioning is just not enough.

As an API provider you should adopt a dependable API evolution strategy that gives your users timely updates and new functionality, makes updating to a your newest version a breeze and joy because of new features instead of a reason for despair because of the amount of work it requires.

As an API user you should demand this so that you can send you patches and actually hope that they will get into the next version in a timely manner while it is not a burden to update to new revisions. And then there is the small part of actually enjoying an API that works and is extended in a good and healthy way.

Consider libraries like jQuery or Apples Cocoa where it is pretty much a no brainer to update to the newest version immediately. At the same time, they stay current and adapt and absorb new emerging patterns in a timely manner while fixing old warts and making the framework more and more consistent all the time. (Ok, some warts are not fixeable, but they try very hard)

I think it is Common Sense to do Dependable API Evolution, but for some reason it hasn't yet caught on as common knowledge - and there is no easy way to refer anyone to a document that explains it. So here I go.

Problems

  • API's suddenly change names / types / behaviour from version to version
  • API's are suddenly removed from version to version without a warning period
  • Bugs/ Inconsistencies in APIs are not fixed
  • New and improved patterns or API designs are not adapted in a timely matter

Extremes to avoid:

  • The Python Standard Library Syndrome: Your Library is so stable that a module has to be dead/ unmaintained for 3-5 years before you consider adding it to your library. Some symptoms: API changes rarely, your API has very inconsistent naming, and design pattern use varies widely across your API. Also many of the non core modules have alternatives in the wild that have a vastly more fluent / short / powerful / consistent / modern APIs. Consider the Python standard library. Almost no development happens in there, many of it's modules are 10 and more years old with only the most required bugfixes going in. Not even the standard naming convention of python is used consistently throughout it's API.

  • The Python 3k Syndrome: Your users stick with an old version of your API and are very reluctant to update. Symptoms: You are forced to release new versions of it instead focussing your engineering on the newest version. You add Interims Versions, i.e evolutions of the old API version to make it easier for your users to switch to the newest version. You release new versions of your new API that add in old features again to make it easier for your users to upgrade. Consider the introduction of Python 3.0. Nobody used it. Three major revisions where required (3.0, 3.1, 3.2) before the community is actually considering its adoption. (And now - 2021 - some 12 years after this text was written, there is still a lot of software around that is on Python 2).

  • The Thousands of Patches Flying in Close Formation Syndrome: Different parts of the API have very different fluency to it and don't match very well. Symptoms: Knowing one part of the API doesn't make it any easier to guess the names and workings of other parts of your API. Documentation cannot be consolidated by talking about the Design Patterns adopted by your framework, but instead is separated from each other and also very needed because you need to look at it for every module anew.

  • The DOM API Syndrome: Different implementations / versions of your API are so inconsistent, that it is almost impossible for users to consume all of them. Symptoms: There are adapter packages around to wrap your API and make it easier for users to actually use them and allow them to target different versions of that API that are in use in the wild. Consider the success of jQuery that does nothing that hiding the DOM-APIs behind something sane and manageable. (2021 me here - the web has started to tackle this problem, but boy, is this still a nightmare. Just the fact that projects like can I use are so much needed
)

  • The Ruby Debugger Syndrome: Different versions of your API change so much that consumers are unable to evolve an API that builds on it. Symptoms: Different versions of your API have different incompatible packages that implement the same functionality in an incompatible way. Backwards compatibility is virtually impossible. Consider the different Ruby-Debugger gems that have sprung up for the various versions of the interpreter. 'ruby-debug' for Ruby 1.8, 'ruby-debugger19' and later 'debugger' for Ruby 1.9, 'byebug' for Ruby 2.0 and it seems Ruby 2.1, already needs another different debugger package. Maybe it is 'pry' now...

  • The Version 2 Rewrite Syndrome: Symptoms: You have a branch in your repository that contains your next version, because it's so much different that you really need to retain a branch of the current version to apply bugfixes while you finish up the next version. Again, Python 2 and 3 comes to mind as an abhorrent example of this problem. 12 years after the release of Python 3.0 the, old version 2 branch was still around and kind of alive as small features where added, bugs fixed. Thats a problem - not a great achievement.

Solutions

  • Have a clear deprecation cycle and use it. It should stretch over multiple versions, and depending on the size or importance of that Deprecated API, this could be several major versions.
  • Your deprecation cycle should contain these steps (which could itself be a multi version rollout)

    • Deprecated APIs are clearly marked in the documentation and source.
    • There is a clear statement in the source or documentation what the expected alternative is. What is the the developer expected to do / use instead?
    • Using a deprecated API should emit a warning.
    • Deprecated API is removed from the documentation. Still using it raises a warning.
    • Switch that warning to an error for developers, while only warning users.
    • Then and only then it should disappear.

    This is the single most important thing to do, everything else follows from this.

  • Don't just change / remove API. You released it? It's out there. Deprecate it but retain it for a time and then remove it! Document what users are expected to do instead. This documentation should be referred to / included in the warning that is raised when that API is used.
  • Document warts that you cannot fix anymore because they are too engrained in the API and are too widely adopted. It is important that your users understand that this is a wart and nothing to use as an example for others or patches that they send you. This ensures that your errors of the past are not repeated.
  • Never miss an opportunity to change your API to make it more uniform and or adopt a deeper pattern across it.
  • Focus a major part of your documentation on the patterns that underly your API and then don't repeat yourself in every part of the documentation but just refer to it.

Do this and your API becomes more and more coherent over time. Users of such an API can often just use a broad lisit of your API packages / objects / methods that are easy to scan / search. With that they can get an overview of what is available at a glance. They will often not need detailed documentation as they can just guess how stuff works, what stuff is named, how error handling works, 
 All of that makes for coding that is enjoyable and code just flows out of your fingers.

Also, users will update in a heartbeat allowing you to actually focus on newer versions, instead of having to maintain long term stable old versions and waste developer time on this rather non productive stuff.

What do you think? Should I set up a webpage like semver.org? Do we need stickers like Dependable API Evolution?

Text will be evolved (on Github)[https://github.com/dwt/Dependable_API_Evolution]

Feedback welcome.

Yeehaw! Endlich wieder ein Blog!

written by Martin HĂ€cker on

Lange hat es gedauert - Ahem.

In den letzten Monaten habe ich mich mit Lektor auseinandergesetzt. Mit ein Paar Plugins (CreativeCommons und Markdown Highlighter) kann man viel Spaß beim Blog-Post schreiben haben.

Der große Vorteil von Lektor ist, dass man eine komplett statische Seite damit rendert. Man kann die Seite danach also ĂŒber ein CDN ausliefern - 'Internet Scale' ist also gar kein Problem


Solche Systeme gibt es natĂŒrlich viele - aber mit Lektor kriegt man zusĂ€tzlich noch ein CMS (das Lokal lĂ€uft) - oder auch hinter .htaccess geschĂŒtzt laufen kann, so dass man keine Sorgen haben muss, dass einem ĂŒber so eine Webseite der Server aufgemacht wird.

Aber Lektor ĂŒberzeugt auch mit einer moderat einfachen API, selbst plugins dafĂŒr zu schreiben ist einfach und macht Spaß. Fast wie es bei Track ganz am Anfang auch einmal war. â˜ș

Ich freue mich darĂŒber dass ich jetzt eine voll-responsive Seite haben, die mit HTML und CSS wirklich auskommt - no JS needed. (Ich werde aber vielleicht in der Zukunft fĂŒr Experimente auch JS in der Seite verwenden). FĂŒr jetzt ist es erst mal das Experiment wie lange ich ohne JS auskomme.

Da wĂ€re zum Beispiel das MenĂŒ - das auf Mobil mit Animation schön aufklappt. Hierzu habe ich verschiedene Techniken kombiniert.

  1. Die VerhĂ€ltnisse der ganzen Abmessungen werden ĂŒber CSS-Variablen und CSS-Calc einmal global gesetzt. Das geht zwar noch nicht vollstĂ€ndig, da man CSS Variablen nicht in Media Queries einsetzen kann, aber immerhin. Einzig dass man Stylesheets noch nicht 'nested' aufschreiben kann stört.

  2. Die Animation beim klick auf das Hamburger MenĂŒ sowie das Öffnen des MenĂŒs kommt komplett ohne JS aus. Damit das geht braucht es ein DOM Element, dass den State 'ein oder ausgeblendet' hĂ€lt, gleichzeitig via CSS abgefragt werden kann sowie diesen State durch Klick auf ein anderes Element Ă€ndert. Well, dafĂŒr funktioniert lustiger Weise das <input type=checkbox id=menu-state> Element. Das wechselt den checked-Zustand, was mann dankenswerter Weise in CSS mittels :checked herausfinden kann. Der Clou ist aber, dass man irgendwo anders im Dokument ein <label for=menu-state> haben kann, auf das man Klicken kann um diesen State zu wechseln. Und <label> kann man im Gegensatz zu <input type=checkbox> in allen Browsern vernĂŒnftig stylen. Und man kann mittels CSS-Animationen alles animieren. Fuck yeah!

Lockless Algorithms

written by Martin HĂ€cker on

Schon lange habe ich keine so passende Visualisierung mehr fĂŒr etwas gesehen:

Lockless Algorithms / Schlosslose Algorithmen:

via devopsreactions

Melderecht Datenweitergabe jetzt ohne Zustimmung

written by Martin HĂ€cker on

Das ist doch ein Juwel: Melderechter sollen in Zukunft meine Daten an jedes Industrieunternehmen weitergeben dĂŒrfen - wenn die Firma die Daten fĂŒr ein "update" schon vorhandener Daten anfragt.

Was pikanterweise die Voraussetzung fast jeder Anfrage ist.

Dem kann man dann gar nicht mehr widersprechen - de fakto hat man also keine Möglichkeit mehr dieser GeschĂ€ftspraktik der MeldeĂ€mter wenigstens fĂŒr sich persönlich Einhalt zu gebieten.

Compact hĂ€lt dagegen und hofft auf 200.000 unterstĂŒtzer in möglichst kurzer Zeit:

http://www.campact.de/melderecht/sn1/signer

DafĂŒr!