Avainsana-arkisto: oppimisanalytiikka

Oppimisanalytiikan hyödyntäminen tulevan kurssitoteutuksen suunnittelussa ja valittujen toteutusratkaisujen validoimisessa 

Johdanto 

Kun opettaja suunnittelee kursseilleen seuraavia toteutuksiahän voi hyödyntää edellisiltä toteutuksilta tekemiään havaintoja. Oppimisanalytiikka antaa opettajalle välineitä pukenäitä havaintoja tilastollisiin muotoihin, jotka sopivasti visualisoituina antavat opettajalle nopeasti tarkkaa ja tiivistä kuvaa kurssin pidemmän aikavälin tapahtumista epätarkkojen muistinvaraisten mielikuvien sijaan. Karelia-ammattikorkeakoulussa on osana eAMK– ja APOAhankkeita kehitelty erilaisia keinoja tuoda Moodle-oppimisympäristöön kytketty oppimisanalytiikka opettajan kurssisuunnittelun tueksi.        

Tavoitteenamme oli hyödyntää oppimisanalytiikkaa kurssin suunnittelussa ja kehittämisessä (mm. käytettävyys, opittavuus, opiskeltavien asioiden järjestys) sekä todentaa sen kautta myös toimivat asiat, joita ei tarvitse muuttaa uuteen toteutukseen. 

Kurssin esittely 

Karelian tietojenkäsittelyn koulutuksen Web- ja mobiilikäyttöliittymät -opintojakso, laajuus 5 opintopistettä, on tarjolla ICT-koulutuspolun opinnoissa Itä-Suomen yliopiston, Savonian ja Karelian opiskelijoille valinnaisena opintojaksona. Valinnaisuus tuo epävarmuutta vuosittaisten toteutusten välille, sillä osallistujien määrä voi vaihdella muutamasta kymmenestä pariin sataan osallistujaan. Lukumäärän suuruusluokka tarkentuu käytännössä opintojakson alkaessa. Tämä tuo haastetta opintojakson toteutuksen suunnitteluun sekä ennalta opettajalle resurssoidun työajan riittävyyden arviointiin työaikasuunnitelmia tehtäessä  

Web- ja mobiilikäyttöliittymät -opintojakso koostuu (1) alkutestitehtävistä, joissa testataan kurssilla tarvittava osaaminen, joka olisi hyvä olla kurssille osallistuvilla, (2) yksittäisistä teoriatehtävistä sekä laajemmasta (3) projektitehtävistä, joihin liittyy mobiiliresponsiivisen websivuston työstäminen lähtien aineiston keruusta, suunnittelusta, toteutuksesta päättyen käytettävyyden arviointiin ja testaukseen. Projektitehtävillä on arvioinnissa vahvempi painotusOpintojakson kaikki tehtävät ovat pakollisia. 

Opintojakso on jaoteltu Moodlessa osioihin (kuva 1), joihin jokaiseen kuuluu tehtäviä. Opintojaksolle on suunniteltu ohjattua palautusta siten, että osioissa pääsee etenemään sen jälkeen kuin edellisen opintojakson tehtävät on palautettu. Alkutestejä lukuun ottamatta tehtävien arviointi tehdään manuaalisesti. 

Web- ja mobiilikäyttöliittymät -opintojakson rakenne

Kuva 1. Web- ja mobiilikäyttöliittymät -opintojakson rakenne.

Opintojakson toinen toteutus alkoi tammikuussa 2020, jolloin aloimme suunnitella analytiikan hyödyntämistä opintojakson kehittämiseksi. Kokemuksia opintojakson toimivuudesta oli jo ensimmäiseltä toteutukselta, mutta halusimme analytiikan avulla selvittää toisaalta, mitä pitää muuttaa opintojaksolla ja toisaalta myös saada vahvistusta omille oletuksillemme opintojakson toimivista kohdista. Kevään 2020 toteutus alkoi Moodlen Collaboraten kautta pidetyllä aloitusluennolla, jossa oli lyhyt johdatus kurssin sisältöön sekä esittely opintojaksolla opiskeluun liittyvistä asioista. Luento tallennettiin. Toteutuksella oli suositusaikataulu, mutta opiskelija pystyi etenemään hieman myös omassa tahdissaan.  

Menetelmän käytön kuvaus 

Analytiikan tarkentamiseksi kohti haluamiamme tavoitteita käytimme Karelia-ammattikorkeakoulussa kehitettyä suunnittelumallia, jonka käytön yleisiä periaatteita on kuvattu tarkemmin Karelian verkkojulkaisun VASUN artikkelissa (Gröhn et al. 2020). Web- ja mobiilikäyttöliittymät -opintojaksolla analytiikan tavoitteena oli keskittyä tarkastelemaan sitä, miten opiskelijoiden taustatiedot ja kurssin osioiden suoritusjärjestys mahdollisesti vaikuttavat kurssin menestyksekkääseen suorittamiseen. Pyrimme löytämään vastauksia erityisesti seuraaviin kysymyksiin:  

  1. Onko osioille varattu työstöaikaa (liian vähän, sopivasti, liian paljon)? 
  2. Mitkä tehtävät pudottavat opiskelijat? 
  3. Miten lähtötasotestin tulokset korreloivat lopullisiin opintojakson tuloksiin? 
  4. Missä järjestyksessä opiskelijat tekevät projektiosion suunnittelu- ja toteutustehtäviä? 

Kahteen ensimmäiseen kysymykseen saatujen vastausten pohjalta pyrimme arvioimaan, nousevatko kurssitehtävien vaikeustasot sopivassa/samassa suhteessa opiskelijoiden tietojen karttumisen kanssa. Kolmannen kysymyksen avulla halusimme selvittää, miten ennakko-osaaminen auttaa opintojaksosta selviytymistä. Analytiikan kolmanteen kysymykseen antamien vastausten pohjalta pohdimmeonko kurssille tarvetta rakentaa erilaisia oppimispolkuja eritaustaisille opiskelijoille. Neljännen kysymyksen vastausten avulla pyrimme varmentamaan, että opiskelijat tekevät projektin eri osiot tietyssä oppimisen kannalta oleellisessa järjestyksessä. 

Havaintoja 

Se, että opiskelija käy Moodlessa avaamassa esimerkiksi jonkin aktiviteetin, ei vielä kerro sitä, kuinka paljon hän käyttää aktiivisesti aikaa kyseisen aktiviteetin parissa. Aika-arvion mittaamista varten onkin säädetty niin sanottu “paras arvaus”. Jos opiskelija avaa Moodlessa aineiston tai aktiviteetin, oletuksena on, että hän viettää sen parissa vähintään 15 minuuttia. Esimerkkikurssin ajallinen seuranta osoitti, että opintojakson suunniteltua viikkorytmiä noudatettiin melko hyvin, mutta tyypillisesti opiskelu painottui lähelle eräpäivää tai eräpäivään. Web-sivuprojektille oli varattu aikaa kaksi viikkoa, joista ensimmäisellä viikolla tapahtumia Moodleen leimautui vähän. Visualisoinnin tulos ei kuitenkaan tässä tapauksessa tarkoita, että opiskelija ei opiskelisi tuona aikana, koska www-sivun tekeminen ei tapahdu Moodlessa.  

Kun tarkastelimme tehtävistä saatuja arvosanoja, havaitsimme, etteivät mitkään yksittäiset tehtävät pudottaneet opiskelijoita. Yhdessä suunnitellun noudattamisen kanssa tämä havainto indikoi, että kurssin kokonaisrytmi ja yksittäisten vaatimustasot ovat tasapainossa keskenään. Tosin kahden tehtävän vaatimustasoa olisi visualisoinnin (kaavio 1) perusteella syytä tarkastella uudelleen, koska riittävää arvosanavaihtelua ei ollut. 

Mitkä tehtävät pudottava opiskelijan?

Kaavio 1. Mitkä tehtävät pudottava opiskelijan?

Ensimmäisen toteutuksen jälkeen opintojaksopalautteista ilmeni, että osalla osallistujista ei ollut tarvittavaa ennakko-osaamista. Ensimmäisellä toteutuksella kurssille ilmoittautuneita oli paljon, mutta myös kurssin kesken jättäneitä oli useita. Toiseen toteutukseen lisäsimme kurssikuvauksen esitietovaatimukset jo ennen oppimisanalytiikan käyttöönottoa. Tämä vähensi opintojaksolle ilmoittautuneiden määrää, mutta toisaalta keskeyttäneiden määrä oli pieni. Tehty korjaus sekä opintojakson ennakkotehtävät ja kurssin koko suorituksen analytiikka (kts. kaavio 1) tukevat ajatusta, että esitietovaatimukset edesauttoivat sopivan opiskelija-aineksen valikoitumista. Ennakkovaatimusten lisääminen yhdenmukaisti osallistujajoukkoa eikä näin ollen erilaisien oppimispolkujen suunnittelulle ole tarvetta.  

Kurssin tehtävistä osa oli erillisiä itsenäisiä kurssitehtäviä ja osa oli yhtenäisen kurssiprojektin yksittäisiä osia. Edellisen toteutuksen jäljiltä havaitsimme, että osa opiskelijoista siirtyy liian nopeasti projektin toteutusvaiheeseen, jättäen suunnitteluvaiheen minimiin. Tämä näkyi esimerkiksi siinä, että opiskelijat palauttivat sivuston rakennetta esittävän vuokaavion sijaan jo toteutetun sivuston. Halusimme selvittää analytiikan avulla, tekevätkö opiskelijat tehtävät oikeassa järjestyksessä ja käyttävät riittävästi aikaa eri osioihin, etenkin projektin suunnittelu- ja toteutustehtävien osalta. Kaavio 2 kertoo, että projektin osalta tehtävät tehdään oikeassa järjestyksessä ja niihin käytetään aikaa.  Oivalsimme, että tehtävien järjestyksen tai osioiden ajastuksen muuttamisen sijaan suunnittelun tärkeyttä pitää korostaa tehtävien uudelleenmuotoilun kautta. Esim. Teettää kaksi erilaista sivustorakennetta esittävää vuokaaviota.  

Tehtävien suoritusjärjestys

Kaavio 2. Tehtävien suoritusjärjestys.

Lopuksi

Kaiken kaikkiaan koimme oppimisanalytiikan antaneen meille mahdollisuuden testata kyseisen opintojakson aiempien toteutusten pohjalta syntyneitä oletuksiamme nykyiseltä toteutukselta kerättyä oppimisdataa vasten ja auttaa tarkentamaan näkemyksiämme kurssin pedagogiikan osalta.  

Yksi opettajia aina kiinnostavista kysymyksistä on, kuinka paljon opiskelija viettää aikaa opittavan asian parissa (mm. tehtävät, annettu materiaali, luentotallenteet). Jos tähän halutaan vastaus analytiikan kautta, se edellyttää analytiikka-ajatuksen sisällyttämistä kaikkeen opintojakson suunnitteluvaiheeseen jo heti alusta alkaen rakennetta mietittäessä, jotta analytiikkaan tarvittavia jälkiä jää  

Kirjoittajat Sanna Kukkasniemi, lehtori ja Seppo Nevalainen, lehtori  

APOA-hanke
ICT-koulutuspolku, ISAT–UEF-yhteistyö 

Lähteet 

Gröhn, A. &  Öhman, M. & Väistö, L. & Suomalainen, M. 2020. Oppimisanalytiikkaa Svensk klinik Rautalankaruotsi -opintojaksolla. Vasu 6/2020. 

Oppimisanalytiikka – merkityksellistä aktiivisuuden visualisointia

Edellisessä oppimisanalytiikkaa käsittelevässä blogimerkinnässäni mainitsin, että esittelen vielä lisäksi erilaisen aktiivisuuskaavion toteutusta, jolla saadaan seurattua opiskelijan edistymistä vaikkapa tietyn aihealueen parissa. Tässä merkinnässä avaan sen taustoja sekä teknistä toteutusta.

Miksi tämä visualisointi tarvitaan?

Ohjelmointi I –opintojaksolle oli tarpeen saada selville opiskelijan opiskelumotivaatio, suhteutettuna ryhmän motivaatioon. Suoraviivaisin lähestymistapa, joka antaa edes jotain osviittaa aiheesta, on Moodlen aktiviteettiraportti.  Sen tarjoama tieto on yleinen ryhmän aktiivisuus kurssin aikana. Se ei kohdistu tiettyyn aihealueeseen, emmekä voi määrittää milloin kukakin opiskelijoista on keskittynyt mihinkin. Lisäksi jälkikäteen tarkasteltuna Moodlen tuottama aktiivisuuskaavion saa näyttämään dataa pelkästään aina tietystä takarajasta tähän päivään saakka, joten sen visuaalinen ulosanti ei ole niin hyvä. Yhtenä rajoituksena voi ajatella olevan myös Moodlen hakuparametri – kaukaisin hakuaika aktiivisuudelle on kaksi vuotta.

Moodlen aktiivisuuskaavio. Sen saa auki kohdasta Raportit -> Tilasto.

Ratkaisun hahmottaminen

Ensimmäiseksi lähdimme liikkeelle hahmottelemalla tulevan käyttöliittymä paperille. Tämän pohjalta keskusteltiin tarpeellisista ja tarpeettomista ominaisuuksista. Havaittiin, että jos on mahdollista valita pelkästään merkityksellisiä tapahtumia, saamme parempaa tietoa aktiivisuudesta. Tätä varten hahmottelimme valintalaatikot käyrään panoksen antaville materiaaleille ja aktiviteeteille.

Karkea käyttöliittymähahmotelma syksyltä 2018

Alustavan hahmottelun jälkeen oli syytä tarkastella mitä on tehtävissä. Esimerkiksi arvosanojen näkyminen käyrällä ei ainakaan tällä tavalla esitettynä tuottanut mitään lisäarvoa, joten ne jätettiin pois. Lisäksi kaikkien tehtävien hakeminen datasta osoittautui hieman hankalaksi tuossa vaiheessa, joten rajasimme ne pois. Lisäksi verbeiksi valittiin oletuksena yleiset selkeää aktiivisuutta ilmaisevat, kuten lähettää, kommentoida, päivittää ja vastata, sekä näiden eri ilmenemistavat xAPI-rekisterissä.

Seuraavaksi voimmekin siirtyä itse kyselyn toteuttamiseen! Asioiden yksinkertaisemiseksi jätämme käsittelemättä web-teknologioilla tapahtuvan tiedon hakemiseen sekä tietoliikenteen suojaukseen liittyvät osat, ja keskitymme itse visualisoinnissa tarvittavaan dataan. Kysely suoritetaan MongoDB-tietokantaan.

Miten data noudetaan tietovarastosta?

Aloitamme kyselyn suorittavan palvelimen puolelta. Visualisoitavaa dataa varten tarvitsemme siis kurssin tapahtumien päiväysten ääripäät, sillä on turhaa visualisoida ajanhetkiä, milloin mitään ei tapahdu. Tätä varten käytämme MongoDB:n aggregaatio-ominaisuutta.

Ensimmäiseksi rajaamme tapahtumat tietyn opintojakson sisälle.

Statements.aggregate([{ 
    $match : { 
      "statement.context.contextActivities.grouping.0.id" : courseUrl
    } 
  },

Tämän jälkeen ryhmittelemme datan uudelleen tapahtumien aikaleimojen perusteella, ja laskemme niistä minimi- sekä maksimiarvon.

 { 
    $group : { 
      _id : "$statement.context.contextActivities.grouping.0.id", 
      maxDate: { $max: "$statement.timestamp"}, 
      minDate: { $min: "$statement.timestamp"} 
    } 
  }],

Palautuneen datan perusteella voimme laskea montako päivää kurssi on kestänyt, ja toteuttaa sen avulla päiväkohtaisten pistekeskiarvojen laskennan.

Seuraavaksi haemme uuden datajoukon. Selvitämme kurssille osallistuvat opiskelijat rajaamalla distinct-hakumääreellä vain yksilölliset nimet annetun kurssin sisältä.

  Statements.distinct('statement.actor.name', { 
    "statement.context.contextActivities.grouping.0.id" : courseUrl 
  },

Kun data on palautunut, laskemme kunkin opiskelijan osalta erillisen summataulukon kertyneille pisteille.

        for( var a=0;a<numStudents;a++)
        {
          var student = {
            name: studentRows[a],
            scores: []
          };
          for(var sc=0;sc<numCourseDays;sc++)  student.scores[sc]=0;
          studentsTmp[student.name] = student;
        }

Itse pisteet saamme seuraavalla kyselyllä, jossa hyväksymme mukaan palautettavaan tietuejoukkoon kaikki aktiivista tekemistä määrittävät verbit. Haku rajatataan  tietyn opintojakson sisälle, ja tuotetun joukon alkiot sisältävät palautuessaan ainoastaan aikaleiman sekä opiskelijan nimen. Tässä kyselyssä on huomioitava, että käytetyt xAPI-verbit ovat järjestelmäkohtaisia, joten on aina syytä varmistaa, miten data on kuvattu eri tietovarastoissa – samaa voidaan ilmaista joskus usemmallakin tavalla.

  Statements.find( 
    { "$and": [ 
      {  "verbs": { $in : [ 
        "http://adlnet.gov/expapi/verbs/scored",
        "http://activitystrea.ms/schema/1.0/join",
        "http://id.tincanapi.com/verb/replied",
        "http://activitystrea.ms/schema/1.0/start",
        "http://activitystrea.ms/schema/1.0/submit",
        "http://activitystrea.ms/schema/1.0/follow",
        "http://activitystrea.ms/schema/1.0/update",
        "http://activitystrea.ms/schema/1.0/attach",
        "http://activitystrea.ms/schema/1.0/complete"
      ]}},
      { "statement.context.contextActivities.grouping.0.id" : courseUrl }}
   ]  
}, 
{    'statement.timestamp': 1,    'statement.actor.name': 1,  } 
)

Nyt kun kasassa on riittävät tiedot, voimme laskea pistekertymän kullekin opiskelijalle päiväkohtaisesti. Käymme läpi edellisestä kyselystä palautuneet tietueet, ja lisäämme kunkin opiskelijan tapahtumapäivälle pisteisiin yhden.

for(var s=0;s<scores.length;s++) 
{ 
   var scoreDay = moment(scores[s].statement.timestamp,"YYYY-MM-DDTHH:mmZ").startOf('day'); 
   if ( scoreDay.isSameOrAfter(start) && scoreDay.isBefore(end)){ 
     var numDaysSinceBeginning = scoreDay.diff(start, 'days'); 
     var studentName = scores[s].statement.actor.name; 
     studentsTmp[studentName].scores[numDaysSinceBeginning] += 1; 
   } 
}

Seuraavaksi laskemme pistekertymistä kumulatiivisen version siten, että aiempien päivien kertyneet pisteet lisätään nykyisen päivän pisteisiin. Samalla laskemme pisteiden summat päiväkohtaisiin keskiarvoihin.

          for(var property in studentsTmp ) 
          { 
            if ( studentsTmp.hasOwnProperty(property)){ 
              if ( studentsTmp[property].scores.length != numCourseDays ) 
                     averageScores[0] += studentsTmp[property].scores[0]; 
                     for(var d=1;d<studentsTmp[property].scores.length;d++) 
                     { 
                       studentsTmp[property].scores[d] += studentsTmp[property].scores[d-1];
                       averageScores[d] += studentsTmp[property].scores[d]; 
                   } 
              } 
          } 

Päiväkohtaiset pistesummat on jaettava vielä opiskelijoiden lukumäärällä keskiarvon laskemiseksi.

for( var day=0;day<averageScores.length;day++) 
{ 
   averageScores[day] /= numStudents; 
}

Tämän jälkeen edessä on ainoastaan palautettavan datan muotoilu selaimen koodissa.

Miten data saadaan selaimeen?

Selaimessa kyselyn lähettäminen tapahtuu seuraavalla tavalla. Muodostamme POST-metodikutsun, jonka avulla lähetetään haettavan opintojakson tunnus kyselyä varten. D3.js-kirjaston mukana tulevan asynkronisen palvelupyyntötoiminnon avulla saamme sen aikaan helposti. Kyselyssä rajaavana parametrina on kurssin URL-osoite, jota aiemmin kuvattu palvelinsovelluksen logiikka käyttää tulosjoukon rajaamisessa.

var url2 = '<PALVELUN URL-OSOITE>; 
d3.json(url2, { 
         method: "POST", 
         body: JSON.stringify({ course: url}), 
         headers: { 
             'Content-Type': 'application/json; charset=utf-8' 
         } 
}).then( function (json) {

then-osuus ottaa vastaan palautuvan datan käsittelyfunktion. Käsittelyfunktiossa ensimmäisenä muodostamme dataan erillisen date-kentän, johon liitämme tekstimuotoisesta aikaleimasta luodun javascriptin päiväysobjektin. Se antaa mahdollisuuden vertailla datan aikaleimoja helposti myöhemmin.

for(var x=0;x<json.length;x++) {
   var parse = d3.timeParse("%Y-%m-%dT%H:%MZ");
   json[x]["date"] = parse(json[x].timestamp); 
}

Tässä yhteydessä otamme käyttöön crossfilter-kirjaston. Muodostamme ensiksi dimensiot (x-akselille tuotettavat arvot) opiskelijoiden nimelle ja päiväykselle. Sen jälkeen laskemme ryhmittelysummat (y-akselille tuotettavat arvot) opiskelijoiden pisteille, sekä ryhmän pisteiden keskiarvoille päiväkohtaisesti.

  var ndx = crossfilter(json); 
  var studentDim = ndx.dimension(d => d.name ); 
  var dayDim = ndx.dimension(function (d) { return d3.timeDay(d.date); }); 
  var scoreDim = ndx.dimension(d => d.value ); 
  var scoresGroup = dayDim.group().reduceSum(d => d.value ); 
  var avgGroup    = dayDim.group().reduceSum(d => d.avg );

Minimi- ja maksimipäiväykset, sekä maksimipisteet saadaan datasta irti seuraavasti:

         var maxDate = d3.timeDay(dayDim.top(1)[0].date);
         var minDate = d3.timeDay(dayDim.bottom(1)[0].date);
         var scoreGroup = scoreDim.group();
         var maxScore = scoreDim.top(1)[0].value;

Opiskelijoiden valinta toteutetaan valikkorakenteella, joka onnistuu dc.js-kirjastolla seuraavasti. Valikkorakennetta voi ajatella ikään kuin omana “kaavionaan”, jossa tehdyt valinnat heijastuvat kaikkiin muihinkin dc.js:n NDX-kontekstilla luotuihin kaavioihin.  Valikon yhteyteen rakennamme toiminnallisuuden, jolla voimme piirtää valitun opiskelijan nimen kaavion oikeaan yläreunaan. Tämä auttaa tarkastelijaa hahmottamaan paremmin kenen datasta onkaan kyse.

var studentMenu = 
    dc.selectMenu('#dc-student-menu') 
      .dimension(studentDim) 
      .group(studentDim.group()) 
      .controlsUseVisibility(true) 
      .on('filtered.monitor', function(chart,filter) { 
          if (filter === null ) $('#name').html("Kaikki opiskelijat"); 
          else $('#name').html(filter);                                        
       });

Palkkikaavio päiväkohtaisia pisteitä varten saadaan aikaan seuraavasti.

var barChart = dc.barChart("#dc-bar-hits"); 
barChart 
  .width(990)
  .height(60) 
  .elasticY(true)
  .elasticX(true)
  .dimension(dayDim)
  .group(scoresGroup)
  .mouseZoomable(true)
  .x(d3.scaleTime().domain([minDate,maxDate]))
  .brushOn(true)
  .xUnits(d3.timeDays);

Koska haluamme esittää kaksi erillistä käyrää samassa kaaviossa, tarvitsemme dc.js:n yhdistelmäkaavion. Sen perusasetuksilla määritämme muodostettavan kaavion koon ja akseleiden ohjetekstit. Lisäksi ns. ohjauskaavioksi (.rangeChart) määritämme aiemmin luodun palkkikaavion, että voimme järkevästä valita tietyn aikavälin tarkasteltavaksi tarkemmin.

var compositeChart = dc.compositeChart('#dc-line-progress');
compositeChart
   .width(990)
   .height(400)
   .mouseZoomable(true)
   .dimension(dayDim)
   .legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
   .elasticY(true)
   .x(d3.scaleTime().domain([minDate,maxDate]))
   .xAxisLabel("Kurssin päivät")
   .yAxisLabel("Kertyneet pisteet")
   .rangeChart(barChart) 

Varsinaiset käyrät tuottavat kaaviot muodostetaan compose-funktiolle välitetyn javascript-taulukon avulla. Kaksi erillistä käyrää saadaan esittämään eri tietoja group-määrityksen avulla. Ensimmäinen esittää valittujen opiskelijoiden pisteiden summaa päiväkohtaisesti (scoresGroup), ja tähän vaikuttaa valikon avulla määritelty opiskelijakohtainen suodatus.

 .compose([ 
     dc.lineChart(compositeChart) 
        .width(990) 
        .height(120)
        .transitionDuration(1000) 
        .dimension(dayDim) 
        .x(d3.scaleTime().domain([minDate,maxDate])) 
        .round(d3.timeDay.round) 
        .xUnits(d3.timeDays) 
        .elasticY(true) 
        .colors('#f77') 
        .group(scoresGroup, "Saavutetut pisteet") 
        .brushOn(false),

Toinen käyristä esittää puolestaan koko ryhmän keskiarvoa. Tähän dataan opiskelijakohtainen suodatus ei vaikuta, sillä jokaiseen opiskelijatietueeseen on tallennettu myös kunkin päivän ryhmäkohtainen keskiarvo.

dc.lineChart(compositeChart) 
    .width(990)
    .height(120) 
    .transitionDuration(1000) 
    .dimension(dayDim) 
    .x(d3.scaleTime().domain([minDate,maxDate])) 
    .elasticY(true) 
    .colors('#ffff00') 
    .group(avgGroup, "Ryhmän keskimää¤rin saavuttamat pisteet") 
    .brushOn(false) 
]) 
.brushOn(false);

Lopuksi käskytämme kaikkia kaavioita piirtymään.

dc.renderAll(); 
}); 
} 

Miten visualisointi auttaa hahmottamaan edistymistä paremmin?

Kun tarkastelemme vaikkapa Moodlen tuottamaa kaaviota ja uutta aktiivisuuskaaviota samasta datasta, on havaittavissa, että jälkimmäinen osoittaa selkeästi paremmin suvanto- ja edistymiskohdat. Ryhmän keskiarvo on kaaviossa esitetty keltaisella värillä, ja opiskelijan puolestaan punaisella. Esimerkiksi tässä tapauksessa voidaan tulkita, että eräs opiskelija on suorittanut kurssia neljän erillisen rupeaman aikana, jotka ovat olleet kestoltaan lyhyitä, mutta niiden aikana on tapahtunut paljon. Kaaviosta paljastuu myös, että opiskelija on työskennellyt hieman jälkijättöisesti ryhmän keskimääräiseen työskentelyyn verrattuna, mutta lopuksi saavuttanut saman aktiivisuustason kuin ryhmä keskimäärin.

Myöhässä tehneen opiskelijan kaavio

Valittaessa erään toisen opiskelijan data tarkasteltavaksi, havaitsemme että tilanne on hieman toisin, ja aktiivisuutta on ollut ryhmän keskiarvoa enemmän heti alusta lähtien. Kuitenkin tällä opiskelijalla on ollut keskivaiheilla pitkiä jaksoja, jolloin aktiivisuus ei ole ollut niin suurta – nähtävästi alussa kiritty matka on antanut tilaa edetä loput kurssista rauhallisemmin.

Alussa aktiivisemman opiskelijan kaavio

Kun edellä olevaa kaaviota verrataan mitä Moodlesta oletuksena saadaan esille, voimme todeta yllä olevan kaavion olevan paljon yksityiskohtaisempi, sekä täsmällisempi. Moodlen kaavio ei erottele opiskelijoita, vaan näyttää kunkin päivän osalta roolikohtaiset tapahtumat yleisesti.  Uusista kaavioista saa tietoa ohjauksen tueksi, ja on helposti nähtävissä, milloin on syytä selvittää opiskelijalta tämän aktiivisuuden laskun syyt.

Mitä tämän jälkeen?

Mikäli samaan kaavioon liitetään eri aihe-alueisiin liittyviä aktiivisuuskäyriä, voimme nähdä entistä paremmin substanssikohtaista etenemistä. Silloin ohjauksen kohdistaminen on yhä täsmällisempää, ja se avaa myös (ainakin teoreettisen) mahdollisuuden niputtaa saman aihealueen parissa ohjausta tarvitsevat samalle ohjauskerralle.

Lisäksi erilaisten tapahtumien pistemäärän painottaminen voisi tulevaisuudessa olla tarpeellinen toiminto. Erilaiset oppimiskäsitykset voivat vaatia aina erilaista tekemistä, jolloin oppimiskäsityksen mukaiset erot pitäisi saada korostetusti esille.

Kirjoittaja Anssi Gröhn, tietojenkäsittelyn lehtori

Oppimisanalytiikan kokeiluja: the data is not enough – tehtävien vaikeusasteen visualisointia

Aiemmassa blogimerkinnässäni Oppimisanalytiikan kokeiluja: kuinka lakkasin olemasta huolissani ja rakastamaan LRS:ää  käsittelin kokemuksia Learning Lockerin hyödyntämisestä. Mainitsin myös, että Learning Lockeria kokeiltiin Karelia-ammattikorkeakoulun kielten opintojen verkkokurssien datan avulla. Seuraavaksi avaan tarkemmin, miten Learning Lockerin ulkopuolisia työkaluja hyödyntäen saatiin aikaan tehtävien vaikeusasetetta kuvaava kaavio Rautalankaruotsia-verkkokurssille.

Miksi tähän on ryhdytty?

Rautalankaruotsia on itseopiskeltava ruotsin kielen verkkokurssi, joka keskittyy kielioppiin ja sisältää runsaasti automaattisesti arvioitavia tehtäviä. Automaattinen arviointi kielten yhteydessä on monilta osin haastavaa, sillä oikean vastauksen voi periaatteessa antaa monella tavalla. Moodlen automaattiset arviointityökalut sen sijaan toimivat yleensä paremmin, kun kysymykseen on tarjolla yksi ainoa oikea vastaus. Tämän vuoksi automaattisen arvioinnin tekeminen vaatii runsaasti työtä, ja siltikin joku vaihtoehto voi jäädä huomiotta. Lisäksi itseopiskeluun suunnatun verkkokurssin tehtävien on oltava selkeitä, etteivät opiskelijat turhaudu.

Rautalankaruotsin ensimmäisten toteutusten yhteydessä tehtävätarkistuksiin oli luonnollisesti jäänyt pieniä virheitä, jotka aiheuttivat opiskelijoille ylimääräistä päänvaivaa. Mutta varmuuden saamiseksi oli syytä visualisoida, mitkä tehtävistä oikeasti vaativat kehittämistä.

Tässä blogimerkinnässä esittelen, millaisella kyselyllä tieto saadaan ulos Learning Lockerin tietokannasta, sekä käyn läpi visualisoinnissa hyödynnetyn dc.js-kirjaston pääpiirteet teknisen toteutuksen näkökulmasta.

Datan hakeminen Learning Lockerin tietovarastosta

Koska Learning Locker on oppimisen tietovarasto, ja se erottelee erilaiset oppimistapahtumat xAPI-rekisterin avulla, voimme saada selville kurssitoteutuskohtaisesti kunkin opiskelijan tehtäväpalautustapahtumat.  Tieto siirrettiin moodlen lokeista Learning Lockeriin erityisen xAPI-työkalun avulla.  Learning Locker on rakennettu MongoDB-tietokannan päälle, joten voimme tehdä kyselyjä sen tietokantaan MongoDB:n tarjoaman ohjelmointirajapinnan avulla.

Hyvä! Lähdemme liikkeelle siitä millaista tietoa haluamme taulukkomuodossa, eli jotain tämän suuntaista:

Tehtävät Ongelmissa (kpl) OK (kpl)
Tehtävä 1 0 54
Tehtävä 2 30 24

Pienen aivojumpan jälkeen on hahmotettavissa, että alkuvaiheessa kaikki tehtäväpalautustiedot on ryhmiteltävä opiskelijan sekä tehtävän nimen perusteella, sekä laskettava tietueeseen yhteen summa opiskelija-tehtävä-palautustapahtumapareista. Tämän avulla saamme selville, montako kertaa opiskelija on yrittänyt kutakin tehtävää. Tästä pääsemmekin jo rakentamaan kyselyä!

Koska joudumme laskemaan tietoja yhteen sekä ryhmittelemään niitä, käytämme apuna MongoDB:n ns. aggregaatio-operaatioita. Se tapahtuu mongoDB:n yhteydessä seuraavalla tavalla:


db.statements.aggregate( [{

Ensimmäisenä haemme tietovarastosta kaikki palautustapahtumat. Verbimäärityksellä voimme rajata palautuvaan tulosjoukkoon pelkät palautukset. Tapahtumat generoinut työkalu käyttää tässä tehtäväpalautuksen yhteydessä xAPI-rekisterin submit-verbiä (http://activitystrea.ms/schema/1.0/submit). Lisäksi rajoitamme tapahtumat tiettyyn kurssiin sen URL-osoitteen perusteella, joka tallennettu tapahtuman kontekstitietotaulukkoon, ja varmistamme että tapahtuma tulee halutusta tietovarastosta.


{
$match: {
"statement.verb.id":"http://activitystrea.ms/schema/1.0/submit",
"statement.context.contextActivities.grouping.0.id" : "KURSSIN_URL_OSOITE",
"lrs_id" : mongoose.Types.ObjectId("TIETOVARASTON_ID")
}
},

Nyt meillä on haettuna kaikki tehtäväpalautusta kuvaavat oppimistapahtumat! Seuraavaksi ryhmittelemme ne opiskelijan ja tehtävän nimen perustella, sekä laskemme yhteen montako kertaa kukin opiskelija-tehtäväpari esiintyi, ja tallennamme tiedon “hits”-muuttujaan.


{
$group: {
_id: {
task: "$statement.object.definition.name.en-GB",
student: "$statement.actor.name"
},
hits: { $sum:1}}
},

Sen jälkeen tarvitsemme raja-arvon, josta päättelemme ketkä opiskelijoista ovat ylittäneet yrityskerroille hyväksyttävän kynnyksen. Käsittelemme muistissa olevan datajoukon, ja lisäämme kuhunkin problems-kentän arvolla tosi tai epätosi, riippuen siitä oliko ennalta määritetty raja ylitetty vai ei.


{
$project: {
"_id.task" : true,
hits: true,
problems : { $gt: ["$hits", RAJA_ARVO ]}
}
},

Tämän jälkeen voimme ryhmitellä tiedon pelkästään tehtävän nimen perusteella, sekä lisätä tehtävätietueeseen ongelmissa olleiden ja normaalisti edenneiden opiskelijoiden määrät laskemalla ne yhteen problems-kentän perusteella:

{
$group : {
_id: "$_id.task",
"numberOfStudentsInProblems": { "$sum": { "$cond": [ { "$eq": [ "$problems", true] }, 1, 0 ]}},
"numberOfStudentsOk": { "$sum": { "$cond": [ { "$eq": [ "$problems", false] }, 1, 0 ]}}
}
}
]);

Kysely palauttaa nyt halutun datan, joka voidaan siirtää HTML-sivulle jollakin menetelmällä, esimerkiksi express.js-kirjastoa hyödyntävän nodejs-sovelluksen avulla. Asioiden yksinkertaistamiseksi sen tekninen esittely, sekä kyselyä varten välitettävien parametrien hyödyntäminen ohitetaan toistaiseksi.

Datan visualisointi

Datasta muodostetaan siis kaavio ja taulukko. Siinä hyödynnämme apuna crossfilter-filtteröintikirjastoa moniulotteiseen datan käsittelyyn, sekä dc.js-kaavionvisualisointikirjastoa. Tätä varten tarvitsemme tietyt javascript-kirjastot mukaan sivustoon:

<script src="d3.js"></script>
<script src="crossfilter.js"></script>
<script src="dc.js"></script>
<script src="d3-queue.js"></script>
<script src="d3-fetch.v1.js"></script>

Rakennetaan seuraavaksi pohja. Ensiksi luodaan HTML-sivulle <div>-elementit palkkikaaviota sekä taulukkoa varten:

<body>
<div id="dc-bar-attempts"></div>
<div id="dc-table-attempts">
<body>

 

Kun data on saatu haettua, muodostetaan crossfilter-kirjaston avulla riippuvuudet sisältävät datajoukot. Määrittelemme tehtävädimension, sekä erilliset ryhmät ongelmatapauksille ja normaaleille seuraavasti:

var ndx = crossfilter(json);
var taskDim = ndx.dimension(d => d._id );
var problemGroup = taskDim.group().reduceSum(d => d.numberOfStudentsInProblems );
var expectedGroup = taskDim.group().reduceSum(d => d.numberOfStudentsOk );

Tämän jälkeen meillä on riittävät yhteydet datan käsittelyyn monissa eri kaavioissa.

Visualisointia varten luomme luomme datasta ensimmäisenä palkkikaavion. Dimensioksi (eli X-akselille tuleviksi arvoiksi) määritetään tehtävät, ja pinoamme kaavioiden y-akselille ongelmallisten sekä normaaleiden opiskelijoiden määrät. Tämä tuottaa meille kaavion, jossa yksi palkki esittää yhden tehtävän tilanteen graafisesti.


var barChart = dc.barChart('#dc-bar-attempts');
barChart
.width(990)
.height(400)
.mouseZoomable(true)
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.elasticX(true)
.elasticY(true)
.x(d3.scaleOrdinal())
.xUnits(dc.units.ordinal)
.xAxisLabel("Tehtävät")
.yAxisLabel("Odotetujen yrityskertojen ja niiden ylitysten suhde")
.dimension(taskDim)
.group( problemGroup, "Ongelmissa olleet")
.stack( expectedGroup, "Normaalit" )

Taulukoon rakennamme kolme saraketta: tehtävän nimi, ongelmallisten tapausten lukumäärä. sekä normaalien tapausten lukumäärä, sekä määritämme lajitteluksi laskevan järjestyksen ongelmallisten tapausten lukumäärän perusteella.


var table = dc.dataTable("#dc-table-attempts");
table
.dimension(taskDim)
.columns([
{ label: 'Tehtävä', format: function(d){ return d._id;} },
{ label: 'Ongelmissa (kpl)', format: function(d){ return d.numberOfStudentsInProblems;} },
{ label: 'OK (kpl)', format: function(d){ return d.numberOfStudentsOk;} }
])
.size(1000)
.group(function(d) {return "yes";})
.showGroups(false)
.sortBy(function(d) {return d.numberOfStudentsInProblems;})
.order(d3.descending);

Taulukon kukin rivi kertoo meille numeeriset arvot, millainen tilanne yksittäisen tehtävän osalta oli palautuskertojen suhteen – montako ongelmallista ja montako normaalia tapausta.
Loppujen lopuksi käskytämme dc.js-kirjastoa piirtämään kaaviot:
dc.renderAll();

Lopputulos palkkikaaviosta voi näyttää sopivalla datajoukolla esimerkiksi tältä. Oranssi palkki kuvaa normaalisti suorittaneiden määrää, ja sininen puolestaan raja-arvon ylittäneiden määrää. Ongelmalliset tehtävät on havaittavissa selkeästi.

Taulukko puolestaan esittää saman tiedon numeerisesti.

Miten kokeilu onnistui?

Visualisoinnin perusteella on helppo havaita mitkä tehtävistä ovat hankalia ja mitkä puolestaan helpompia. Myös yksityiskohtaiset tehtävänimet ja numeeriset arvot näkyvissä taulukosta. Kaavio ja taulukko tarjoavat suoraan tiedon, mitä tehtävistä on kehitettävä, joten kehitettyä visualisointia voi pitää varsin onnistuneena. Visualisointi on myös tarpeen mukaan suoraan siirrettävissä muihin opintojaksototeutuksiin, joissa on paljon automaattisesti tarkistettavia tehtäviä. Tämä tekee siitä varsin hyödyllisen työkalun suunnittelun tueksi.

Myöhemmin tänä keväänä esittelen erilaisen aktiivisuuskaavion toteutusta, jolla saadaan seurattua opiskelijan edistymistä vaikkapa tietyn aihealueen parissa.

Kirjoittaja tietojenkäsittelyn lehtori Anssi Gröhn

(21.3.2019 muokattu kuvia ja tekstiä)

Oppimisanalytiikan kokeiluja: kuinka lakkasin olemasta huolissani ja rakastamaan LRS:ää

Learning Locker on Learning Record Store (LRS) -järjestelmä, jolle ehkäpä kuvaavin käännös on oppimistapahtumatietueiden tallennuspaikka. Learning Lockerin on kehittänyt HT2 Labs, ja järjestelmä on saatavilla niin avoimena lähdekoodina, kuin maksullisena pilvipalvelunakin. Learning Locker hyödyntää oppimistapahtumien tallentamisessa sähköistä oppimiseen liittyvää ohjelmointirajapintaa. Rajapinnasta käytetään nimitystä xAPI, tai Experience API tai TinCan API. XAPIn tapahtumat esitetään verkkosovellusten usein suosimassa tiedostomuodossa (JSON).

Learning Locker mahdollistaa erilaisista ulkoisista järjestelmistä koostetun tiedon yhdistämisen samaan tietovarastoon jatkokäsittelyä varten, sekä tarjoaa muutamia erilaisia visualisointityökaluja datan käsittelyyn. Maksullisessa versiossa visualisointityökaluja on tarjolla kattavammin. Learning Lockerin dokumentaation (2018) mukaan se on integroitavissa laajennosten avulla erilaisiin ulkoisiin oppimisalustoihin, kuten Moodleen, Blackboard Learniin ja ehkäpä Suomessa vähemmän tunnettuun Articulateen. Lisäksi tarjolla on Yammer-laajennos.

Miksi Learning Locker?

Learning Lockerin valintaan vaikuttivat erityisesti avoin lähdekoodi, ilmaisuus ja mahdollisuus asentaa se paikallisesti omille palvelimille – erityisesti EU-GDPR:n asettamien vaatimusten osalta pilvipalveluiden käyttäminen ei ole enää niin suoraviivaista. Myös Weissin (2017) mukaan Learning Locker on sijoittunut vuonna 2017 neljänneksi yhdeksän vertaillun LRS-järjestelmän kesken.

Learning Lockerin ominaisuuksista sai vaikutelman kattavasta ratkaisusta, jolla olisi mahdollista tallentaa oppimisanalytiikkadata sekä visualisoida se helposti. Lisäksi xAPIn avulla tapahtuva tiedon siirtäminen eri organisaatioiden LRS-järjestelmien kesken olisi helpompaa. Tästä olisi erityisesti hyötyä ristiinopiskelussa, sillä opiskelijoille esitettävän oppimisanalytiikkadatan on katettava kaikki hänen suorituksensa – riippumatta siitä, missä oppilaitoksissa hän opiskelee.

Learning Lockerin tietovarasto on toteutettu MongoDB:n avulla, joten sen sisältämää tietoa on mahdollista käyttää ulkoisissa visualisointityökaluissa kohtalaisen helposti – olettaen toki, että hallussa on ohjelmointiosaamista.

Kokemukset

Ensimmäiset kokemukset Learning Lockerista ovat varsin myönteisiä; sinällään järjestelmän käyttöönotto ja hyödyntäminen on varsin suoraviivaista. Hankalinta on ehkä saada järjestelmään merkityksellistä dataa järjestelmään sisälle. Kuten tulikin jo mainittua, Moodle-oppimisympäristöön on saatavilla erillinen laajennos, jonka voi konfiguroida lähettämään Moodlen keräämät tapahtumat Learning Locker-palvelimelle. Tämä vaatii myös palvelimen näkyviin julkisesti verkkoon, joten jätin tämän vaiheen pois ja rakensin erillisen työkalun, joka koostaa xAPI-tapahtumat Moodlen lokitiedostojen perusteella ja lähettää ne halutulle Learning Locker –palvelimelle, jota voi ajaa vaikkapa paikallisesti virtuaalikoneessa.

Pilotoinnin perusteella Learning Lockerin visualisointityökalut ovat erinomaisia nopeita testejä sekä yksinkertaisia visualisointeja varten, joissa dataa ei tarvitse yhdistellä tai käsitellä erityisesti – näkyvää saa aikaan muutamassa minuutissa, kuten esimerkiksi alla olevan aktiivisuutta esittävän kaavioyhdistelmän. Mikäli tarvetta on monimutkaisemmille kyselyille, on syytä käyttää muita työkaluja visualisoinnin tukena.

Tietojen siirrettävyys saa apuja Learning Lockerin käytöstä; minkä tahansa kyselyn tiedot voi siirtää CSV-muodossa olevaan tekstitiedostoon. Tekstitiedosto sisältää JSON-muodossa kunkin kyselyn palauttaman xAPI-tapahtuman, joten se on pienellä vaivalla mahdollista lukea ohjelmallisesti sekä siirtää toiseen xAPI-rajapinnan toteuttavaan järjestelmään.

Miten tästä eteenpäin?

Learning Lockeria on kokeiltu Karelia-ammattikorkeakoulun tietojenkäsittelyn sekä kielten opintojen verkkokurssien datan avulla. Vuonna 2018 olemme hyödyntäneet Learning Lockeria ja xAPIa visualisointiin liittyvissä piloteissa, ja oppimisanalytiikkadatan hallinnassa järjestelmä on osoittautunut varsin toimivaksi.

Teknisestä näkökulmasta olemme jo hyvin perillä Learning Lockerin ja xAPIn mahdollisuuksista. Jatkamme jo alkaneiden pilottien parissa, sekä jakamaan niistä saatuja tuloksia laajemmin. Toki edessä on vielä selvitystyötä rajanpintojen parissa, kuten esimerkiksi Peppi-järjestelmän sisältämän datan integrointi Learning Lockeriin.

Kirjoittaja Anssi Gröhn, tietojenkäsittelyn lehtori