Kategori Mitt i veckan

84% av alla kunder är nöjda med Slajm

Idag skriver jag åter om statistik. Denna gång fokuserar jag på proportioner med fokus på en parameter. Ibland vill man göra en undersökning som fokuserar på hur stor andel av en population som ger lyckosamma utfall. Det kan handla om hur många produkter från en tillverkningsprocess som ej är defekta, eller det kan handla om hur många väljare som kan tänka sig rösta för ett särskilt alternativ. Denna gång återvänder jag till Slajm-farmaren för att undersöka hur stor andel av hans kunder som är nöjda med den slajm som han säljer.

Vi tar ett urval med storlek n=250. Antalet nöjda kunder är X=210. För att räkna ut andel nöjda kunder i vårat urval så har vi :

Vi vill gärna kunna säga något om alla kunder, inte bara kunderna i vårat stickprov. Vi väljer att svara med ett konfidensintervall på en konfidensnivå 95%. För att räkna ut intervallet så har vi vår andel (0,84) plus/minus en marginal:

Marginalen räknas ut med hjälp av det kritiska värdet z* som tas fram för den angivna konfidensnivån 95%. Det ger oss z*=1,96. Det kritiska värdet här är givet vid en särskild konfidensnivå när vi har en t-fördelning. Det liknar antalet standardavvikelser från medelvärdet i en normalfördelning. Det kritiska värdet multipliceras med standardfelet för urvalet. Det ger oss följande uträkning:

Vi har alltså att 84% av kunderna är nöjda med Slajm, med en felmarginal på 5% på en 95%-konfidensnivå. Slajm-farmaren Frans är inte helt nöjd med detta, för det finns en risk att han inte har uppnått en kundnöjdhet över 80%. Frans har lite att tänka över nu. Varför finns det så många kunder som inte är nöjda? Det är ett mysterium som Frans får utreda framöver. Tills dess är vi klara för den här gången.

Nästa gång, på fredag, tänker jag återvända till programmering och skriva ett par rader om det. Hur enkelt var det egentligen att jobba med Symfony och gick det bra att ladda upp applikationen på ett webbhotell? Vi ses då!


Ska jag bry mig om 1 ml hit eller dit?

Äntligen är det dags: hypoteser! Som jag förklarade i måndags så skulle jag återkomma till ämnet idag, om jag så fann tid till det. Och det gjorde jag!

I förra veckan skrev jag om konfidensintervall. Då beskrev jag hur stor andel av populationen (eller sannolikhet kan vi säga) som hade ett visst värde inom ett intervall. Ämnet med hypoteser är besläktat med det. Låt mig förklara.

Det fanns en gång en slajm-tillverkare, farmaren Frans. Slajmen skördade han från sina slajmklumpars saliv. Ju roligare de har desto mer slajm! Målet var att varje slamklump skulle producera 100 ml saliv per dag, men farmaren hade svårt att räkna alla klumpars produktion. Frans tog hjälp av sin granne, den sifferkunnige Samantha. Sam visste att produktionen per klump i snitt avvek med cirka 1 ml, men hon visste inte om produktonen i överlag faktiskt var 100 ml per klump. Sam och Frans valde slumpvist ut 20 klumpar en dag och såg att de producerade 99 ml per klump. De upptäckte då att dessa klumpar producerade 1 ml mindre slajm per dag än de 100 ml som Frans hoppades på. Men det kanske bara var slumpen som spelade in. Kan Frans ignorera att hans slumpvist valda klumpar producerade mindre än han hoppades? Sam tog fram en penna och papper och skrev upp följande:

Sam förklarade att även om det bara handlade om 1 ml, så vart det sannolikt så att alla klumpars produktion var lägre än önskat. Det var nämligen 1,25% chans att produktionen inte var lägre än 100 ml.

Frans tackade Sam så mycket, för nu visste han att han behövde göra dagarna roligare och dräggligare för slajmplumparna. Dagen därpå började Frans bygga vattenrutschkanor och fontäner. Slajmet som producerades veckan därpå översteg 100 ml per klump.

Historien om Frans och Sam handlade om hypoteser. Frans uttryckte att han skulle ha 100 ml saliv per klump (eller mer). Vi kan kalla detta för nollhypotesen. Den alternativa hypotesen är att produktionen är mindre än det. Detta kallas för en ensidig hypotes. Alternativet är en tvåsidig hypotes och det är när den alternativa hypotesen kan anta värden som är såväl mindre som större än nollhypotesen.

Genom att visa att nollhypotesen inte var sann (med 1,25% sannolikhet för att påståendet inte är korrekt), så fastställde Sam att produktionen var för låg mot vad Frans önskade.

Detta var min första stapplande förklaring om hypoteser, nästa gång nämner jag något om signifikansnivå och annat kul. Vi ses då!


Normalfördelning

Som jag skrev om i måndags så skulle jag återkomma till en särskild form av symmetriska kurvor, nämligen normalkurvorna. Några grundläggande egenskaper med normalkurvorna är att de är symmetriska, klockformade och har bara en topp. Vi har då en enkel beskrivning av en normalfördelning. I mitten av normalkurvan är medelvärdet. Den representeras av μ. Dessutom kan man faktiskt se standardavvikelsen. Den kan man få genom att titta var normalkurvan är som brantast och ta avståndet från µ till den punkten på x-axeln. Standardavvikelsen representeras av σ. I bilden nedan så är en skiss på en normalkurva med både medelvärde och standardavvikelse markerade.

För en normalfördelning så kommer toppen alltid att vara där medelvärdet är. Förändring av medelvärdet innebär att normalkurvan flyttar med den förändringen längs x-axeln. Om standardavvikelsen förändras så kommer bredden av kurvan förändras. En lägre standardavvikelse innebär en smalare kurva. På bilden nedan har jag skissat hur det kan se ut.

Så vad är det då som är så bra med normalkurvor? För det första är det en beskrivning på en ganska vanligt förekommande fördelning ute i det vilda. För det andra så beskriver det ganska bra ett antal olika slumpmässiga utfall.

En intressant egenhet med normalkurvor är att inom 1σ från µ så finns 68% av alla observationer. Vid 2 och 3σ från µ så finns 95% respektive 99,7% av alla observationer. Vad dessa förhållanden innebär för något som kallas standardiserade värden kommer jag avsluta veckan med. Vi ses då!


Statistikprogramvara

Nu börjar jag komma igång med enklare analyser av data med hjälp av programvaran Jamovi. Programvaran är gratis och community driven. Det innebär att engagerade R-utvecklare kan bidra med att utveckla olika moduler till programmet. I detta inlägg kommer jag beskriva några enkla beståndsdelar och analyser man kan göra med programmet.

Först är det bra att ha ett dataunderlag att jobba med. Jag tänkte jobba med ett exempel här från SCB:s Statistikdatabas. Ett exempel jag väljer här är import/export inom tjänstehandel:

Exempel på vilken data man kan välja att ladda ned från SCB, samt i vilket filformat.

Jag väljer att vilka variabler jag vill inkludera och laddar ned dem som en relationstabell. Det innebär att jag kan öppna upp filen och kan göra analyser i Jamovi.

Så jag öppnar upp den nedladdade filen i Jamovi och kontrollerar att variablerna har rätt datatyp. Jag har fyra variabler och tillser att respektive variabel får rätt mät-typ: export/import är nominal (alltså kategorisk), land är också nominal, år sätter jag till ordinal (då den visserligen finns på en kontinuerlig skala men i detta fall så anser jag att man inte får ut någon vettig data av att dela upp respektive år i fraktioner), och slutligen finns variabeln tjänstehandel i miljoner kronor som är kontinuerlig.

I denna initiala analys vill jag kolla på hur fördelningen mellan import och export såg ut 2017. Jag gör därför en deskriptiv analys i programmet och väljer variabeln för tjänstehandel och delar upp den på år och import/export:

Median för import respektive export 2017:

Vi hade alltså 1.054 miljarder kronor i export och 1.235 miljarder kronor i import. Detta innebär att länderna som ingår i underlaget fördelar sig 50/50 runt dessa värden för deras tjänstehandel.

Jag får återkomma när jag kan göra ännu roligare analyser!


Lista av recensioner och lista av böcker

Nu har jag i applikationen (Bokarenan) fått till såväl en lista av rekommenderade recensioner såväl som ett urval av de senaste recenserade böckerna. Jag påstod i föregående inlägg att jag denna vecka skulle få till en lista med de böcker med bäst betyg. Det kan fortfarande bli så men jag fokuserade istället på att få till de senaste recenserade böckerna.

För att få till urvalet av de senaste recenserade böckerna så har jag fått gå till Repository-klassen för Review. Där har jag lagt till funktionen findByLatest(int $limit). Genom den funktionen så hämtar jag de senaste x-antal recensionerna. Utifrån den samlingen av recensioner så hämtar jag referenserna till de böcker som de refererar. Eftersom en bok kan recenseras flera gånger så använder jag mig av php-funktionen array_unique(). Därmed så tar jag bort överflödiga referenser och kan presentera en bok inte fler än en gång på framsidan.

Det jag vill göra härnäst är att skapa en personlig lista av böcker att läsa. Och från den listan kan man sedan välja att recensera den bok man vill.


Kommer i form

Sagan fortsätter. Senast hade jag problem med att uppdatera användares roller. Rollerna avgör vad användare kan göra på hemsidan. I huvudsak gäller det om en användare ska vara administratör eller inte. När jag skrev senaste inlägget så lyckades jag inte med att uppdatera användares roller och någonstans låg problemet i administratörskontrollen (controller) eller i formuläret. Jag visste inte exakt var problemet låg, men det gör jag nu: det var i båda!

Så jag är lite av en härmapa när jag håller på att lära mig nya saker, och när det kom till användare så härmade jag hur upplägget såg ut för att uppdatera till exempel författare. Och här är kruxet: upplägget för författare och användare är olika. För författare så färdiggenereras en formulärklass som man kan skicka till Twig, vilket är en så kallad template engine. Det innebär att utifrån givna parametrar så genererar Twig den html-kod som behövs för att visa givet innehåll. Och jag byggde upp en formulärklass för användare också, men använde inte Twig till att generera den. Istället så skräddarsydde jag html-koden för att motsvara exakt vad som behövs för att uppdatera användare med rätt roller. I ett parallellt spår med detta så byggde jag controller enligt samma sätt som controller för författare. Det innebar att controllern kontrollerade om det färdiggenererade formuläret innehöll någon ny data. Och eftersom jag inte använde mig av det färdiggenererade formuläret så innebar det att controllern inte fick någon uppdaterad data. Lösningen blev därför att uppdatera controllern med att kolla i svarsobjektet istället för i formulärobjektet. Och därmed hade jag på 5 minuter löst ett problem som hållit mig fast i ett par timmer.

Ytterligare utveckling denna veckan har varit att lägga till en aktiveringsprocess när man registrerar sig som ny användare. Det innebär att applikationen kommer kontrollera att den som registrerat sig också är användare av den epost som har angetts. På den fronten har det gått snabbt, men det beror också på att jag kunnat sitta i åtminstone 20 minuter åt gången för att programmera.

På återseende på fredag!


Auktorisering aktiverad

Så har det visat sig igen att det goda folket bakom Symfony kan göra bra guider. Jag har nu implementerat funktion för att avgränsa ytor av applikation till relevanta roller: så som att man måste vara inloggad som användare för att kunna göra inlägg, och som administratör så finns lite fler verktyg för att hantera entiteter. Det som återstår att göra är att skapa en super-administratör som kan lägga till roller för olika användare.

När jag är klar med super-administratören så finns det lite finlir jag behöver ta hand om. Till exempel behöver jag anpassa kontrollerna för entiteter så att de motsvarar hur jag vill använda dem. I och med dessa anpassningar behöver jag också justera form-klasser och presentationen av formulären.

Jag får se därefter hur mycket tid till övers jag kommer ha framöver. Snart kommer jag att börja en deltids kurs för dataanalys på grundläggande nivå. Det innebär att mina framtida uppdateringar från och med november kommer prata om statistik och att jag redan är inkörd på sömnbrist i och med bebisar.


Grunderna kommer på plats

Nu går det framåt, men är det snyggt? Med hjälp av Symfony så har jag fått formulär som är anpassade mot respektive entitet som jag sparar i min databas. Som jag tidigare beskrivit så vill jag att användaren ska kunna lägga till all relevant data när en recension ska läggas till. Det innebär att flera entiteter kan komma att läggas till när en recension läggs till, så som förläggare och författare. Min första lösning var att lägga till alla förproducerade formulär som Symfony skapat, men det innebar att ett antal fält skapades som förekom flertalet gånger (eftersom varje enskild entitet kunde relatera till en annan entitet). Jag tog därför och gjorde anpassade formulär för respektive entitet som avhandlade just den enskilda entiteten utan dess relation till andra entiteter. Resultatet blev något vackrare.

Så tillbaka till ett tidigare dilemma; hur ska jag implementera funktionaliteten att lägga till fält för fler författare? Ska jag använda min tidigare lösning som exkluderar JavaScript. Detta skulle innebära att sidan laddas snabbare och att jag undviker att använda sådant som jag inte är van vid. Nackdelen är att jag måste i så fall lägga till fält för upp till ett max antal författare för en bok, säg 10 stycken. Oavsett om användaren vill lägga till en författare eller 20. Det skulle kunna i udda fall kunna innebära att användare inte får lägga till så många författare som användaren vill.

Den andra lösningen, så som även beskrivs i en guide från Symfony, är att använda JavaScript och JQuery för att dynamiskt lägga till fler fält. Nackdelen är att källkoden inte blir vacker och sidan kan ta längre tid att ladda. Fördelen är att endast så pass många fält som användaren vill ha finns till hands. Det innebär också något tydligare indelning på ansvarsfördelning vad avser att JavaScript hanterar beteende och låter HTML stå för stommen.

Jag har valt den andra lösning, för att jag vill pröva något nytt och kanske lära mig något nytt. Resultatet nu är som sådant:

Det är tydligt att jag inte jobbat något med utseende, utan endast innehåll och funktion. När jag fått till de grundläggande funktionerna för applikationen så kommer jag ta hand om utseendet.


getData funkade!

Från föregående inlägg så undrade jag om det rätta sättet att komma åt rätt värden från formuläret var att använda Request-objektet eller Form-objektet. I andra projekt hade jag använt ”superglobala” variabler för att komma åt formulär-värden. Då hade jag använt $__POST[”book_name”]. I Symfony finns dessa värden i Request-objektet. Eller så var i alla fall min uppfattning. Men jag upptäckte att jag inte fick det resultat jag väntade för jag fick aldrig värdet från book_name. Därför blev det att jag vände mig till googling:

Det blev helt enkelt ganska många fönster när jag försökte hitta svar på mina funderingar. Jag kollade i Symfonys guider, dess github-sida, PHP:s officiella sida, och så klart Stack Overflow. Jag fick inte riktigt svar på varför det inte funkade att använda Request-objektet. Men däremot fann jag lösningen! Jag kunde använda Form-objektet. Genom $form->getData() så kunde jag extrahera ett bok-objekt. Och därmed kunde jag få detta enkla resultat och jag kände mig så lättad:

Book is set. It's title is Testing.


Gör det enkla

När jag skulle börja göra ett navigationsfält för Bokarenan med hjälp av Twig så hamnar jag lite vilse. Jag vill nämligen ha ett navigationsfält med huvudsidorna för applikationen synliga med indikation för den sida som för tillfället visas. Här har jag tänkt mig att texten kommer göras fetare och bakgrunden något mörkare. I övrigt kommer navigationsfältet vara likadant överallt på sidan. Navigationsfältet kommer därför återanvändas överallt genom att det inkluderas i huvudmallen, av vilka övriga mallar kommer bygga vidare på.

Tidigare har jag använt enkel PHP för att göra en kontroll efter vad en variabel har för värde. I navigationsfältet hade jag om-kontroller för varje sak i fältet för att kolla om variabeln överensstämde med kontroll-värdet.
<div class="<?php if ($page == "start") {echo ' active';}?>">

Det förutsätter dock att variabeln har ett värde som är unikt för varje sida, varje sida måste därmed sätta variabeln före det att koden för navigationsfältet inkluderades i filen:
$page = "start";
include($path_navbar);

Det funkar okej, men är något av spagetti för mig, eftersom jag inte skickar variabeln någonstans utan förutsätter att den inkluderade koden har rätt variabelnamn för att hitta rätt.

När jag nu använder mig av Twig-mallar för att generera min presentation av applikationen så behöver jag ändra logiken något. Jag vill ju att respektive sida ska kunna anmäla sig som den aktuella sidan och huvudmallen ska därmed reflektera detta i navigationsfältet. Jag googlade lite och blev lite skrämd av svaren. Någon styrde det genom att skicka en variabel till den aktuella sidan genom en kontroller. Om den personen hade flera olika kontroller som sedan förlängde en ursprungskontroll med den funktionen eller om samma logik fick användas om på flera olika ställen framgick inte. Visst är det ju så att kontroller är det som styr vad som presenteras när, men jag kände mig inte helt bekväm med att för tillfället använda kontroller för något som jag trodde var ganska enkelt.

Min lösning för närvarande blev istället att huvudmallen har block för respektive navigationslänk. En sida kan därmed ange vilket block i huvudmallen som ska indikeras som aktiv.
Huvudmallen:
<li class="{% block navHome %}{% endblock %}"><a href="/">Hem</a></li>

Undermallen:
{% block navHome %}active{% endblock %}

Det får jobbet gjort. Men jag är inte helt nöjd med det. Det som är bra med detta är att jag fått bort om-kontroller och att jag inte behöver oroa mig över en vilsen variabel. Det som jag inte är nöjd med är att jag kommer behöva hoppa mellan två olika mallar för att hålla koll på alla olika block.