Aanbevolen producten in de Pricewatch

Door ACM op vrijdag 25 juni 2010 21:25 - Reacties (8)
Categorie: Pricewatch, Views: 4.610

In februari introduceerden we vergelijkbare producten. Door de specificaties van producten in een categorie te vergelijken kunnen we de 'euclidische afstand' tussen producten bepalen.
Vervolgens pakken we daar de beste matches uit en laten hetzelfde algoritme los op de recente bezoekersinteresse in de producten. Het idee daarachter is tenslotte dat bezoekers, die een bepaald type product zoeken, meerdere vergelijkbare producten bezoeken. En in de praktijk klopt dat ook :)

Wel bleek de afgelopen tijd dat er nogal zwaar werd gefocused op onbenullige specificaties. De specificaties die bovendien ook nog het vaakst incompleet of inconsistent ingevuld waren. Daar hebben we de afgelopen tijd nog wat aan getuned. De telefoons zijn daarbij een mooi voorbeeld omdat ze erg veel specificaties en filters kennen. Voorheen zag je allerlei telefoons waarvan het totaal niet duidelijk was waarom ze er nou eigenlijk bij stonden, terwijl belangrijke aspecten - zoals de schermgrootte, het type scherm en het OS - nauwelijks meespeelden. Nu zie je telefoons die echt behoorlijk goed vergelijkbaar zijn. Uiteraard hopen we zo de bezoeker nog beter door het doolhof van de productkeuzes te kunnen helpen.

Hetzelfde algoritme bleek ook toepasbaar op los bezoekersgedrag. Op die manier kunnen we van een individuele bezoeker proberen wat producten aan te bieden waar hij mogelijk interesse in heeft. We weten tenslotte wat hij zoal bekeken heeft en wat anderen, die diezelfde producten ook bekeken, bekeken hebben.
Als je bijvoorbeeld geintereseerd bent in de Samsung Galaxy S, HTC Desire en Google Nexus One. Dan zal het er op de pricewatch-index ongeveer zo uit kunnen zien:
Aanbevolen producten


Ik kan me goed voorstellen dat anderen ook de zilveren Desire, iPhone 4 en HTC Wildfire hebben bekeken en, inderdaad, ik heb ze nog niet bekeken. Uiteraard verschilt het gedrag met andere producten. Het is sowieso maar net hoe populair de producten zijn die je bekijkt en wat voor willekeurig gedrag bezoekers ermee hebben.

Als je bijvoorbeeld een populair fototoestel en een nas-oplossing bekijkt, ziet het er ongeveer zo uit:
Aanbevolen producten, 2e voorbeeld


Mocht je het zelf willen bekijken, hou er dan rekening mee dat er gebruik wordt gemaakt van je sessie. Oftewel, als je veel verschillende producten bekijkt zal er vanzelf een chaos ontstaan die zich vooral richt op de populairste producten (je kan uiteraard een andere browser starten om een schone sessie te krijgen). Verder kijken we voorlopig alleen naar heel recent bezoekgedrag, de afgelopen 24 uur. Hoewel ik wel verwacht dat we dat nog een stukje willen oprekken is het niet bedoeling dat we je met maanden aan bezoekgedrag blijven confronteren. Om die reden krijg je ook geen producten te zien die je reeds zelf bezocht hebt, het bestaan daarvan weet je tenslotte al.
Overigens zit er een vertraging van maximaal 5 minuten in de weergave, het is vrij kostbaar om dit helemaal on-the-fly uit te rekenen, dus een groot deel van het voorwerk wordt elke vijf minuten gecached :)
En als laatste, als je nog niks bezocht hebt, krijg je toch resultaten te zien, dan krijg je domweg populaire producten te zien, net als het blokje op de frontpage. Dit zijn tenslotte de producten waarvan we weten dat veel andere bezoekers er in geinteresseerd waren, dus deze hebben dan de grootste kans om de interesse van de bezoeker te wekken.

Ik hoor graag jullie mening over de werking en verbeterpunten van deze nieuwe functionaliteit. Echter zit ik niet zo te wachten op allerlei discussies over privacy, door de korte periode waarover we de hiervoor gebruikte gegevens bewaren zie ik geen reden je daar heel erg zorgen om te maken. Voorts worden de gegevens anoniem en enkel in massaverwerking gebruikt, de individuele gegevens gebruiken we slechts alleen om het lijstje voor de individu die te pagina bekijkt te genereren. Dus ik hoop dat we dat aspect bij deze kunnen laten voor wat het is :)

Vernieuwde prijsfilters in de Pricewatch

Door ACM op maandag 29 maart 2010 22:42 - Reacties (8)
Categorie: Pricewatch, Views: 3.999

Met "Pricewatch 3.0" werden vele nieuwe verbeteringen gebracht. Sommige daarvan werkten gelijk goed, anderen zijn ondertussen wat bijgewerkt en een enkele wijziging behoeft nog steeds wat tweaks.

Vandaag hebben we na wat tuning de prijsslider vernieuwd. Het grootste probleem daarmee was dat er af en toe grote verschillen tussen de laagste en hoogste prijs voor bepaalde productcategorieŽn zijn. Goede voorbeelden hiervan zijn de monitoren en TV's, maar ook andere categorieŽn zoals videokaarten en harde schijven hadden hier in meer of mindere mate last van.

Het oude algoritme probeerde namelijk ťťn "mooie" lijn met lineair verloop voor het grootste deel van de prijzen te maken. Dat had als gevolg dat er vaak voor de goedkopere, populairste groep van producten hooguit twee stappen in de slider beschikbaar waren, terwijl voor de dure, minder populaire producten er in steeds minder nuttig detail op prijs kon worden gefiltered.

De nieuwe code probeert het prijsbereik in stukken te hakken en maakt, min of meer logaritmisch, meerdere "lijnstukken" om het prijsbereik wat beter af te dekken. Hierdoor komen er in het deel met de populaire lage prijzen (veel) meer stappen ter beschikking, terwijl het stuk met de hogere prijzen in minder, grotere, stappen wordt opgehakt.

Om het wat duidelijker te maken heb ik twee plaatjes van de prijzen van monitoren gemaakt en daar de nieuwe filterstappen bij opgenomen.
Nieuwe filtering monitoren logaritmisch
Bij deze eerste zie je met een logaritmisch prijsbereik duidelijk hoe een heel klein deel van de prijzen een gigantisch deel van het prijsbereik pakt. Met 1500 euro zit je al op 95% van de monitoren af en de laatste 5% gaat dan nog door tot bijna 60.000 euro... Het oude bereik had stappen van 200 euro voor de monitoren, ik heb hier 100 genomen, maar desondanks zie je dat juist in de eerste helft van de grafiek de rode lijn maar twee stappen heeft. Terwijl de nieuwe situatie, met de gele lijn, al diverse stapjes heeft en veel minder afwijkt van de prijzen (blauwe lijn) eronder.

Nieuwe filtering monitoren lineair
Dit is dezelfde data, maar dan van 0 - 95%, hier is ook weer te zien dat in de eerste 80% van de monitoren de gele lijn beter overeenkomt met de blauwe. Pas vanaf 750 euro komt de oude situatie serieus in het voordeel doordat de nieuwe code daar blijkbaar een overgang van 750 naar 2000 euro pakt. Dan hebben we het dus over 162 van de 841 monitoren, maar dit keer de minder populaire. Slechts 3 van de top 50, 6 van de top 100 en 9 van de top 150 monitoren zit boven de 750 euro...
De meeste prijsdifferentiatie is nu mogelijk bij het deel van de producten waar dat het meeste nodig is. Kortom, ik vind het een verbetering :)

Betere performance voor pChart

Door ACM op dinsdag 20 oktober 2009 19:10 - Reacties (22)
Categorie: Pricewatch, Views: 4.172

Met het Pricewatch 3.0-project hebben we ook nieuwe grafiekjes geintroduceerd. Op onze schaal, zowel kwa data als aantallen grafieken, is de performance van zelfs dat soort details redelijk belangrijk.

Mijn eerste implementatie van een grafiekje met pChart-omgeving bleek 3 seconde nodig te hebben op onze testserver om een csv-bestand van 300 regels (met ieder een label en 3 datapunten) te vertalen naar een grafiekje. Nadere inspectie met xdebug gaf aan dat een groot deel van die tijd zat in de opslag van data in het pData-object.
Deze heeft een vrij inefficiente interne datastructuur, maar heeft ook nog eens exponentiele tijd nodig om datapunten toe te voegen. Effectief werd er voor elk nieuw datapunt (n+1) n maal geteld hoeveel elementen er al in de array zaten. Om 3x 300 datapunten toe te voegen ben je dan dus aardig wat count-operaties verder :X Als ik het goed uitrekenen zit dat op 3* 45150 count-operaties. Domweg het buiten de loop verplaatsen van de count-operatie leverde al een volle seconde tijdswinst op.
Het vervangen van de behoorlijk inefficiente loops door een simpele toevoeging van een array per datarij leverde nog wat winst op.

Verder stikt de code van loze dubbele if's, zoals dit soort dingen:

PHP:
1
2
3
4
5
6
7
8
if ( $DataDescription["Format"]["Y"] == "time" )
         $Value = $this->ToTime($Value);
if ( $DataDescription["Format"]["Y"] == "date" )
         $Value = $this->ToDate($Value);
if ( $DataDescription["Format"]["Y"] == "metric" )
         $Value = $this->ToMetric($Value);
if ( $DataDescription["Format"]["Y"] == "currency" )
         $Value = $this->ToCurrency($Value);


Als je weet dat die waarde "time" heeft, hoef je de andere if's niet meer uit te voeren... In dit geval had de code zelfs nog vervangen kunnen worden door een switch-case structuur, maar ik vond de toevoeging van else's voldoende.

Ook stikte de code van de duplicate floor-calls, waar in deze context een cast naar int sowieso al voldoende was. Voor elke alfatransparante die getekend moest worden, heb ik deze niet zo efficiente constructie vervangen:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
$Xi   = floor($X);
     $Yi   = floor($Y);
 
      if ( $Xi == $X && $Yi == $Y)
       {
// ...
       }
      else
       {
       $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
        if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
 
       $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
        if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
 
       $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
        if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
 
       $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
        if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
       }
     }

// Vervangen door:
     $Xi   = (int)$X;
     $Yi   = (int)$Y;
 
      if ( $Xi == $X && $Yi == $Y)
       {
// ..
       }
      else
       {
        $xdiff = ($X - $Xi);
        $ydiff = ($Y - $Yi);
       $Alpha1 = (((1 - $xdiff) * (1 - $ydiff) * 100) / 100) * $Alpha;
        if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
 
       $Alpha2 = (($xdiff * (1 - $ydiff) * 100) / 100) * $Alpha;
        if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
 
       $Alpha3 = (((1 - $xdiff) * $ydiff * 100) / 100) * $Alpha;
        if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
 
       $Alpha4 = (($xdiff * $ydiff * 100) / 100) * $Alpha;
        if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
       }
     }



Het eindresultaat van de wijzigingen vindt je in deze patch, let er daarbij op dat ik de .class-files hernoemd heb naar .class.php. Verder zou ie op pChart 1.27d moeten werken. Aangezien we .class als extentie al voor java's class files hebben gereserveerd maakte dat het editten van die files in eclipse met pdt zo lastig :)

Het eindresultaat was dat de grafiek op de vrij langzame testserver minder dan een seconde nodig had... Het kan vast nog steeds wel wat efficienter, maar de ergste knelpunten zijn er voor ons nu in ieder geval uit.

Snellere Pricewatch-engine: wat resultaten

Door ACM op dinsdag 12 mei 2009 21:28 - Reacties (7)
Categorie: Pricewatch, Views: 4.431

Op vrijdag 20 februari introduceerden we een nieuwe "Pricewatch Engine". Het doel van deze nieuwe engine was om de categoriepagina's in de pricewatch te optimaliseren, zodat de serverside parsetime wat beter werden en het geheel een stuk beter geschikt werd voor toekomstige verdere uitbouw van de mogelijkheden met de specificaties. Een aantal van die uitbreidingen zijn ondertussen geÔmplementeerd en dat bleek inderdaad eenvoudig uit te voeren te zijn, maar daar mag ik uiteraard nu niet verder over uitwijden :P

Ondanks wat kleine bugs bleek de boel goed te werken en dat gaf mij het vertrouwen om direct de dag volgend op de introductie al het aandeel voor de nieuwe engine te verhogen naar 100%. Ik was eerder van plan dat een week lang op te bouwen:
Daarbij sturen we in het begin slechts 10 procent van de categoriepagina's via de nieuwe engine, en als alles goed werkt verhogen we dat stapsgewijs in de loop van volgende week tot 100 procent.
En er zijn uiteraard resultaten, de op een na zwaarste pagina in totale servertijd was het categorieoverzicht van de laptops. Die is na de introductie van de nieuwe engine gezakt naar de 5e plek. Hier een plaatje van de gemiddelde totale servertijd, cputijd, databasetijd en memcachedtijd van een paar dagen voor en een paar dagen na de invoering.

Zoals je ziet is de tijd van ongeveer 0,7 seconde na een kleine gerelateerde optimalisatie al gedaald naar 0,55 seconde om daarna verder door te dalen naar 0,12 seconde. De grootste verschillen die ik heb gezien zaten zelfs in de 85% reductie in servertijd. Uiteraard is met name de factor cputijd ingeruild voor een (kleiner) stukje wachttijd op de Java-engine.
Pricewatch Laptop-categorieen

De impact van de snellere categorieŽn is ook op de algemene cijfers van de pricewatch terug te zien. Een daling van gemiddeld 0,13 naar 0,08 seconde.
Pricewatch totaal