Klynger af stop

I forbindelse med at jeg har arbejdet på at finde naboerne til et stop har jeg osse indført noget der hedder "klynger" af stop. En klynge er nogle stop i nærheden af hinanden der hedder det samme. Det er næsten altid par af stop, et på hver side af vejen, men ikke altid. Den største klynge er Slagelse St. (Ndr.Stationsvej) hvor der er 17 stop lige ved siden af hinanden der alle hedder det samme:

Problemet før var at når man søgte efter et stop fik man ofte flere og fordi de hedder det samme er det ikke til at hitte ud af hvilket der er det rigtige. Efter den næste opdatering bliver stop i den samme klynge ikke vist individuelt men kun en gang. Her er et eksempel på før og efter:

Det burde gøre det lidt nemmere at finde det man leder efter.

Naboer

Jeg er gået i gang med at lave nogle nye funktioner i app'en. En stor del af arbejdet handler ikke om at tilføje noget som sådan men at få de funktioner der allerede er der til at virke mere hensigtsmæssigt. Men der er osse nogle nye ting man kommer til at kunne.

En ting jeg allerede har arbejdet med men som fortjener at blive gjort endnu bedre er at tage med i betragtning når man kan gå mellem stop. Lige nu er app'en ret striks omkring hvilket stop mener og kigger ikke altid på stop i nærheden. Det er jeg i gang med at gøre den lidt mere afslappet omkring.

For at kunne gøre det har app'en brug for at vide hvilke stop der er i nærheden af hinanden. Jeg kalder det at stoppene er naboer. Til det jeg skal lave er det nyttigt at vide for et givet stop, hvilke andre stop er der indenfor sådan rimelig gå afstand. Lige nu bruger jeg 1,25 km som radius – det er det man kan gå på et kvarter hvis man går 5 km/t. Der er i omegnen af 30.000 stop i Danmark og mange af dem, specielt inde i byerne, har mange naboer indenfor 1,25 km. Rekorden er Sankt Markus Allé & Vodroffsvej på Frederiksberg der har 149 andre stop indenfor 1,25 km. I alt er der 4,7 millioner "naboskaber", altså par af stop der er naboer. Jeg vil gerne gemme listen af naboer for hvert stop som en del af app'en for den er ret tung at beregne men 4,7 millioner er alt for meget – det ville fylde mere end hele resten af køreplanerne. Det jeg har arbejdet på denne uge er at løse det problem: hvordan gemmer jeg naboskaberne mellem stop uden at det fylder for meget.

Den løsning jeg er endt med at lave udnytter at når stop er i nærheden af hinanden har de gerne de samme andre stop som naboer. Det kan godt være Sankt Markus Allé & Vodroffsvej har 149 naboer og Danas Plads har 144 men fordi de ligger lige klods op og ned af hinanden er langt de fleste af de naboer de samme for de to. Måske 130 af dem er naboer til begge stop. Hvis jeg gemte en særskilt liste af naboer for hver af de to stop ville der være 293 i alt (149 + 144) men hvis jeg laver en liste med de 130 naboer de har til fælles og deler den mellem de to fylder det kun 163 tal (130 + 19 + 14). 19 er det antal stop Sankt Markus Allé & Vodroffsvej har ud over de 130 de begge har og 14 er det samme for Danas Plads. Det er en pladsbesparelse på næsten 50%.

Det har krævet lidt snilde at få til at virke og specielt at få systemet til selv at kunne genkende hvornår det er en god ide at dele naboer mellem flere stop. Men nu fungerer det og sparer så meget plads at det er realistisk at gemme nabo-listerne sammen med køreplanerne. Stoppet lige udenfor mit kontor bruger f.eks 3 delte nabo-lister som andre stop i nærheden osse henviser til.

Hvert screenshot viser en af de delte lister. De mørke stop er dem der indgår i listen, de lyse er andre stop der henviser til den liste. Her er et andet eksempel, det er et tilfældigt stop i Roskilde der osse bruger 3 delte nabo-lister

Meget af tiden når jeg arbejder på algoritmer går det op i meget abstrakte begreber: tal, afstande, afbildninger, scorings-funktioner, osv, ting der når jeg arbejder med dem ligger fjernt fra det praktiske problem jeg er i gang med at løse. Men når det så virker er det virkelig interessant at tage de løsninger algoritmen finder på og kigge på dem på et kort. Jeg kan ikke forklare præcis hvorfor der er tre delte nabo-lister lige der i Roskilde men det ser da meget fornuftigt ud.

Kosmetiske ændringer

I løbet af sidste uge har jeg lavet forskellige små-ændringer til app'en. Blandt andet ikonet som jeg nævnte tidligere. Den mest synlige ændring er nok at man ikke længere kan vælge til/fra på et google maps kort. Man kan næsten det samme men den nye måde at søge på google maps er blevet lidt mere skrabet. Her er hvordan det så ud før:

Og her er efter:
De minder om hinanden men kortet er væk. Det er fordi google har ændret på deres betingelser så man (det vil sige jeg) nu skal betale for at lave auto-complete og at vise kort. Det er jeg sådan set okay med og den nye udgave laver netop auto-complete som koster en smule. Men at vise kort koster bare uforholdsmæssigt meget så det er jeg blevet nødt til at slå fra.

Nyt android ikon

En af de ting man opdager når man laver en app er at man til en vis grad skal løbe for at stå stille. Google ændrer f.eks løbende kravene til android apps, de opfinder nye ting og fjerner gamle ting. Nogen gange er det noget man skal forholde sig til og bruge tid på bare for at ens app kan blive ved med at køre. Det er det samme med Apple og iPhone apps.

Det nyeste tiltag er at app ikoner skal fungere anderledes på android. Hvor det i gamle dage bare var et billede i en bestemt størrelse skal det nu være mere kompliceret. Det er for at de kan vise dit ikon med forskellige former, runde, firkantede, rund-oval, hvad ved jeg. Og animere dem på fancy vis. Det ser sådan set meget fedt ud men det kræver at jeg laver et nyt ikon i deres nye format. Så har jeg samtidig benyttet lejligheden til at ændre det en smule. Ikke meget, bare en anelse.

Brugerstatistik

Denne post er længere og måske mere tør end sædvanligt. Det er fordi den handler om noget jeg synes er virkelig vigtigt så jeg vil gerne være ekstra åben og detaljeret omkring det.

Den sidste uge har jeg arbejdet på fejlrapportering og brugerstatistik. Første gang google fjernede A til B fra play store var det fordi jeg ikke havde slået googles indbyggede fejlrapportering og brugerstatistik fra. Det gjorde jeg dengang, slog begge dele fra, så jeg stod uden begge dele. Dengang skrev jeg mit eget system til fejlrapportering så jeg har helt styr på at der ikke bliver gemt information om brugere derigennem, men jeg lavede ikke noget system til brugerstatistik. Det slog jeg fra uden at have en erstatning fordi jeg først skulle finde ud af hvordan jeg overhovedet gør det.

Brugerstatistik er viden om hvordan brugerne faktisk bruger din app. Hvis man har en fysisk forretning får man automatisk en masse viden om ens kunder: man kan se hvor de går hen, hvilke varer de kigger på, hvilke varer de køber, hvilke spørgsmål de stiller, osv. Den viden er super nyttig. Fordi A til B er en app får jeg i udgangspunktet intet at vide om hvordan folk bruger den. Hvilke funktioner bruger de? Hvordan bruger de dem? Fungerer de godt eller dårligt? Er folk forvirrede eller synes de det er nemt? I udgangspunktet har jeg ingen anelse. Det er der forskellige løsninger på og en af dem er at opsamle statistik om hvad folk gør.

Det der gør brugerstatistik svært, ikke kun for mig men for alle der lave en app, er at det rammer et spændingfelt mellem faktorer der trækker i forskellige retninger. Blandt andet:
  1. hvad brugerne er interesseret i at app udvikleren ved
  2. hvad udvikleren har brug for at vide
  3. hvad udvikleren har lyst til at vide
  4. hvad lovgivningen så kræver af udvikleren

Typisk er brugeren interesseret i at udvikleren ved så lidt som muligt om dem. Omvendt har udvikleren lyst til at vide så meget de overhovedet kan. Samtidig kræver loven at hvis udvikleren gemmer så detaljerede data om brugere at de er personhenførbare så skal brugeren have en masse rettigheder over dem. Så hvad gør man?

De fleste apps og websites "løser" problemet ved i det store hele at ignorere punkt 1 og 4 og gå all in på punkt 2 og 3. At ignorere punkt 1 har ikke de store konsekvenser, antallet af folk der som mig er villige til ikke at bruge en app fordi den overvåger dem er forsvindende lille. Man skulle tro at punkt 4 var sværere at ignorere men det er næsten lige så nemt: det er der første gang du bruger app'en hvor du trykker godkend til at langt juridisk dokument du ikke har læst, og så må de gøre hvad de vil med dine data. (Eller, jeg tror faktisk ikke et øjeblik på at det holder i retten men det gør ingen praktisk forskel så længe ingen trækker virksomheder i retten over det.)

Allerhelst ville jeg slet ikke at samle brugerstatistik men efter det meste af et år uden det må jeg bare indse at det er virkelig svært at leve uden. Det billede jeg har haft i baghovedet da jeg designede det var: nogen steder er der sådan nogen standere ved siden af cykelstierne der tæller hvor mange cyklister der er kørt forbi. Systemet ved ikke at det er mig der er kørt forbi, det eneste den holder styr på er at en eller anden er. Fra et privacy synspunkt er det helt fint med mig og jeg mener ikke det er urimeligt at opsamle den slags. Det jeg har forsøgt er at opsamle statistik i app'en i samme ånd.

Helt konkret den måde det virker er at app'en holder styr på hvilke dage du bruger den, og hvilke uger, måneder, og år. Det er bare internt i app'en, det bliver i første omgang ikke sendt nogen steder. Så f.eks hvis du har brugt app'en torsdag, lørdag, og søndag sidste uge ville den internt, på din telefon, have noteret at du brugte den sådan her:

  1. Mindst en gang på dagen torsdag den 2. maj 2019
  2. Mindst en gang på dagen lørdag den 4. maj 2019
  3. Mindst en gang på dagen søndag den  5. maj 2019
  4. Mindst en gang i ugen der starter 29. april
  5. Mindst en gang i måneden maj 2019
  6. Mindst en gang i året 2019

Hvor mange gange du bruger den, om det er 1 eller 100, er ligemeget.

På et tidspunkt den følgende uge, lad os sige det er den 8. maj i dette tilfælde, kigger app'en igennem det den har noteret og ser om der er noget omkring perioder der er overstået. I så fald sender den en besked med den information tilbage til en server jeg har sat op. I dette tilfælde ville den sende de 5 første elementer på listen fordi den 8. maj er vi forbi de dage og vi er inde i den næste uge og måned. Det sidste element kan ikke sendes endnu fordi vi stadig er i året 2019. Den besked der bliver sendt indeholder intet om hvem det drejer sig om, kun en at en eller anden ukendt bruger derude der brugte app'en i de angivne perioder.

Serveren har en tæller for hver periode og den holder styr på hvor mange har brugt app'en mindst en gang for hver givet dag, uge, måned, osv. Og altså ikke per bruger, men samlet for alle brugere. Når serveren modtager en ny besked løber den igennem og lægger 1 til for hver periode der er nævnt. Det er alt hvad den gemmer, derefter smider den beskeden væk.

Det vil sige at jeg kan ikke følge nogen brugere individuelt det eneste jeg kan se er hvor mange brugere der har været indenfor en given periode. Præcis ligesom cykel tælleren bare holder styr på hvor mange der er cyklet forbi. Med tiden kommer jeg til at tilføje flere ting der bliver noteret, f.eks hvis jeg tilføjer en ny funktion vil jeg gerne vide hvor mange der har brugt den indenfor en given periode. Og jeg kan osse inddele data med noget ekstra information f.eks android version. Det svarer til hvis cykel tælleren ikke kun talte hvor mange der var passeret men delte dem op i to grupper, de der kørte over og de der kørte under 25 km/t. Det giver lidt mere information men princippet er det samme, der bliver aldrig gemt information for specifikke brugere men kun et samlet tal for alle brugere. Brugerne bliver bare underopdelt, f.eks efter android version.

Den måde jeg ser resultaterne er med et værktøj jeg osse lavede sidste uge, wot:

$ wot stats ls
--- start_activity / day ---
2019-05-02 1
2019-05-04 1
2019-05-05 1

--- start_activity / week ---
2019-04-29 1

--- start_activity_by_version:27 / week ---
2019-04-29 1
Det skal læses som: den 2., 4., og 5., maj var der i alt 1 bruger der brugte app'en, det var der osse ugen der startede 29. april (dvs sidste uge). Og sidste uge var der en bruger der havde android 27 installeret der brugte app'en. (Jeg har testet koden på min egen telefon sidste uge så brugeren er i alle tilfælde mig selv.)

For at demonstrere hvor lidt data der bliver gemt er her et kig på de underliggende data som ligger til grund for det værktøjet viser:


Næste gang der kommen en opdatering af køreplanerne, forhåbentlig denne uge, opdaterer jeg app'en og så bliver statistikken rullet ud til alle brugere. Med tiden tænker jeg at lave en indstilling så man kan slå det helt fra fordi selv om jeg synes det er uproblematisk at opsamle ved jeg der er folk der er ligeglade med hvad jeg synes, de vil bare kunne slå det fra. Og det er i princippet fair nok. Men det bliver ikke lige det næste jeg laver.