Kategori Mitt i veckan

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.


Jag testar och experimenterar

Det råkade bli testning inkluderat, som jag beskrev i mitt förra inlägg. Jag har inte mycket erfarenhet av att testa, men jag hade några enklare JUnit-tester när jag höll på med Java, och efter att ha mixtrat lite med PHPUnit så skulle jag påstå att jag uppnått samma låga kompetensnivå i det.

Såsom jag jobbar nu blir det att jag lägger till ett test och sedan den metod som testet kollar. Jag började med att testa att repository gör det den ska, men det kändes något onödigt. Anledningen: jag vill testa min kod, inte någon annans. Och med tanke på att Symfony kommer med Doctrine så fanns det egentligen inget där att testa för mig förrän jag inkluderade mina egna funktioner. Så de första stegen var att se lite som uppvärmning.

Snart så inkluderade jag ändå egna metoder, och mycket riktigt så hade jag skrivit test för dem. En sådan metod var att repository skulle söka med så kallade wildcards. Enligt testet så funkar det galant.

Testen startar jag från konsolen med utgångspunkt från mitt projekts mapp med instruktionen ./bin/phpunit, och som svar får jag OK (5 tests, 5 assertions). Som ni kan se är jag fortfarande i startgroparna, men det känns bra i magen när man ser testen köras och kan ändra ett fel till ett rätt.

Något som är helt nytt för mig är att testa att Controller presenterar korrekt sida till klienter. Symfony Docs är mig behjälplig och med instruktioner där ifrån så har jag mitt första test klart:
public function testShowHome() {        
$client = static::createClient();
    $client->request('GET', '/');
    $this->assertEquals(200, 

$client->getResponse()->getStatusCode());    
}

Testet kollar att HTTP-statuskoden är 200, vilket vill säga att korrekt sida hittats och levererats till klienten. I detta exempel så kollar testen helt enkelt om index-sidan hittas.