Salta el contingut

Categorització

Exercici categorisation-annex.pdf · PDF

Obres un cofre en un joc i en surt una espasa. Et llegeixes el percentatge de drop, "0.37%"? No. Mires el color del nom. Comuna, Rara, Èpica, Llegendària. Una sola paraula i ja saps si te la quedes.

Algú va decidir on acaba "Rara" i on comença "Èpica". Aquella persona va convertir milers de números possibles en quatre etiquetes, perquè tu pogueres decidir en mig segon. En aquesta pàgina fas la mateixa feina amb games: 199 preus diferents es converteixen en cinc franges, i cada joc rep un nivell segons la mida de la seva audiència.

Què aprendràs

  • Explicar per què convertim un número en una categoria, i què es perd en fer-ho
  • Escriure un CASE WHEN amb diverses branques i predir quina branca agafa una fila concreta
  • Explicar per què importa l'ordre de les branques, i on acaba un NULL
  • Construir price_band i owners_tier, i ordenar-los en el seu ordre lògic en lloc de l'alfabètic
  • Detectar una categoria escrita de dues maneres i fusionar-les
  • Evitar la trampa: les franges no són "a les dades", les has dibuixat tu

De números a nivells de raresa

Compta quants preus diferents té la taula:

SELECT COUNT(DISTINCT price) FROM games;   -- 199

199 preus diferents. Dibuixa una barra per preu i obtens 199 barres: il·legible. Ningú llegeix "drop rate 0.37%". Tothom llegeix "Èpic".

Una categoria és una etiqueta que posa un valor dins d'un grup. Categoritzar és substituir molts valors diferents per unes poques etiquetes.

Un interval (o franja, en anglès bin o band) és un d'aquests grups quan el valor original és un número: un rang amb un límit inferior i un de superior.

L'analogia de tota la pàgina és el nivell de raresa: una categoria és un número que s'ha fet llegible.

El número La categoria
Al joc Drop rate 0.37% Èpic
A games Preu 9.99 5 to 15
Serveix per Calcular: sumar, mitjana, ordenar Llegir, agrupar, comparar grups
Quants valors 199 preus 5 franges

Compte: guarda també el número

Categoritzar no substitueix el preu. Afegeix una etiqueta al costat, en una columna nova. Sempre pots tallar un número en franges noves més endavant. Mai no pots recuperar el número a partir d'una etiqueta.

CASE WHEN: la regla de classificació

CASE WHEN és una columna calculada amb condicions: per a cada fila dona l'etiqueta de la primera condició que és certa.

L'esquelet:

CASE WHEN condition_1 THEN 'label 1'
     WHEN condition_2 THEN 'label 2'
     ELSE 'label for the rest' END AS new_column

Cada línia WHEN ... THEN ... és una branca. Tres regles ho decideixen tot:

Regla Què vol dir
De dalt a baix Cada fila prova les branques en ordre i s'atura a la primera que és certa. L'ordre forma part de la lògica
ELSE agafa la resta Tot el que no ha encaixat enlloc, incloent-hi allò en què no havies pensat. Sense ELSE, la resta es converteix en NULL
NULL mai no és cert NULL < 5 no és cert ni fals: és desconegut. Un NULL passa per totes les comparacions fins a arribar a ELSE
flowchart TB
    R["<b>Una fila</b><br>price = ?"] --> W1{"price IS NULL?"}
    W1 -- "cert" --> L1["'Free'"]
    W1 -- "no cert" --> W2{"price menor que 5?"}
    W2 -- "cert" --> L2["'Under 5'"]
    W2 -- "no cert" --> W3{"price menor que 15?"}
    W3 -- "cert" --> L3["'5 to 15'"]
    W3 -- "no cert" --> W4{"price menor que 30?"}
    W4 -- "cert" --> L4["'15 to 30'"]
    W4 -- "no cert" --> E["<b>ELSE</b><br>'30 or more'"]

El joc gratuït que semblava car

Prova la regla més simple possible amb tres jocs que coneixes:

SELECT game, price,
       CASE WHEN price < 5 THEN 'Under 5' ELSE '5 or more' END AS band
FROM games WHERE game IN ('Half-Life 2', 'Dota 2', 'Portal') ORDER BY game;
game price band
Dota 2 5 or more
Half-Life 2 9.99 5 or more
Portal 9.99 5 or more

Dota 2 és gratuït. Ha rebut la franja 5 or more. El seu preu és NULL, NULL < 5 no és cert, així que la fila ha caigut a ELSE. El joc amb més propietaris de tota la taula, i gratuït, etiquetat com a car. Cap missatge d'error.

Compte: ELSE no vol dir els cars

ELSE vol dir tota la resta, i "tota la resta" inclou les cel·les buides. Si un NULL mereix la seva pròpia etiqueta, anomena'l primer, a propòsit, amb WHEN column IS NULL.

Construir price_band

Cinc franges, preus en dòlars dels Estats Units. El cas NULL va primer:

SELECT CASE WHEN price IS NULL THEN 'Free'
            WHEN price < 5  THEN 'Under 5'
            WHEN price < 15 THEN '5 to 15'
            WHEN price < 30 THEN '15 to 30'
            ELSE '30 or more' END AS price_band,
       COUNT(*) AS n
FROM games WHERE game IS NOT NULL
GROUP BY price_band ORDER BY MIN(price) NULLS FIRST;
price_band n
Free 3094
Under 5 11613
5 to 15 9046
15 to 30 2367
30 or more 565
%%{init: {"themeVariables": {"xyChart": {"plotColorPalette": "#4051b5"}}}}%%
xychart-beta
    title "Jocs per franja de preu"
    x-axis ["Free", "Under 5", "5 to 15", "15 to 30", "30 or more"]
    y-axis "Jocs" 0 --> 12000
    bar [3094, 11613, 9046, 2367, 565]

Cinc files, que sumen 26,685. Cinc barres en lloc de 199.

Fixa't en la branca WHEN price < 15 THEN '5 to 15'. No diu price >= 5 AND price < 15. No cal: una fila només arriba a aquesta branca si price < 5 no ha estat cert. L'ordre ja fa aquesta feina.

Trenca-la dues vegades, a propòsit

La millor manera d'entendre les regles és fer malbé la consulta i mirar què passa.

Trencament 1 — mou una branca a dalt de tot. Posa WHEN price < 30 THEN '15 to 30' per sobre de totes les altres:

price_band n
15 to 30 23026
Free 3094
30 or more 565

Tots els jocs de menys de 30 dòlars s'aturen a la primera branca. Under 5 i 5 to 15 han desaparegut: 23,026 jocs porten ara l'etiqueta equivocada.

Trencament 2 — esborra la branca Free. Torna a posar l'ordre i treu la línia WHEN price IS NULL:

price_band n
Under 5 11613
5 to 15 9046
30 or more 3659
15 to 30 2367

Els 3,094 jocs gratuïts han caigut a ELSE i s'han unit als 565 cars: 3,659 jocs "de 30 o més". Les dues consultes trencades s'executen sense cap error. Això és, una altra vegada, l'error silenciós de la lliçó de tipus.

Error Què passa Com te n'adones
Una condició ampla posada primer S'empassa les files pensades per a les branques de sota Falten algunes franges al resultat
Cap branca per a NULL Les cel·les buides cauen a ELSE La franja de ELSE és sospitosament gran
Una errada en una etiqueta ('5 to 15 ') Es converteix en una categoria pròpia Una franja de més

Ordenar les categories en el seu ordre lògic

Per què ORDER BY MIN(price) i no ORDER BY price_band? Perquè una etiqueta és text, i el text s'ordena com un diccionari:

ORDER BY price_band ORDER BY MIN(price) NULLS FIRST
15 to 30 Free
30 or more Under 5
5 to 15 5 to 15
Free 15 to 30
Under 5 30 or more

Alfabèticament, 15 to 30 va primer, perquè el caràcter 1 va abans que 3 i 5. Per obtenir l'ordre lògic ordenes per alguna cosa que encara és un número: el preu més baix dins de cada franja. NULLS FIRST posa a dalt els jocs gratuïts, que no tenen preu.

Construir owners_tier

La segona categoria descriu l'audiència, amb el owners_min construït a la lliçó anterior. Tres nivells:

Nivell Regla Significat
Niche owners_min per sota de 20,000 El rang més baix: entre 0 i 20,000 propietaris
Mid owners_min per sota d'1,000,000 De 20,000 fins a un milió
Hit tota la resta Un milió de propietaris o més

Una columna creada en un SELECT no es pot reutilitzar en aquell mateix SELECT, així que l'expressió de owners_min s'escriu de nou dins del CASE:

CASE WHEN REGEXP_REPLACE(SPLIT_PART(owners, '..', 1), '[^0-9]', '', 'g')::bigint < 20000
       THEN 'Niche'
     WHEN REGEXP_REPLACE(SPLIT_PART(owners, '..', 1), '[^0-9]', '', 'g')::bigint < 1000000
       THEN 'Mid'
     ELSE 'Hit' END AS owners_tier,

Afegit a la consulta neta (el bloc ara es diu step3) i comptat amb SELECT owners_tier, COUNT(*) AS n FROM step3 GROUP BY owners_tier ORDER BY MIN(owners_min);:

owners_tier n
Niche 17921
Mid 8141
Hit 623

Dos de cada tres jocs de la taula són Niche. Només 623 de 26,685 són un Hit.

Compte: recorda la unitat

Niche no vol dir "gairebé ningú no l'ha comprat". Vol dir que l'estimació diu "entre 0 i 20,000 propietaris". Un nivell construït sobre un límit inferior hereta la seva unitat.

Creuar dues categories

Les categories existeixen per ser creuades. Amb les dues columnes a step3, una sola consulta respon "quina franja de preu produeix més hits?":

SELECT price_band, COUNT(*) AS games,
       SUM(CASE WHEN owners_tier = 'Hit' THEN 1 ELSE 0 END) AS hits,
       ROUND(100.0 * SUM(CASE WHEN owners_tier = 'Hit' THEN 1 ELSE 0 END) / COUNT(*), 1) AS hit_pct
FROM step3 GROUP BY price_band ORDER BY MIN(price) NULLS FIRST;
price_band games hits hit_pct
Free 3094 182 5.9
Under 5 11613 47 0.4
5 to 15 9046 168 1.9
15 to 30 2367 167 7.1
30 or more 565 59 10.4

El SUM(CASE ... THEN 1 ELSE 0 END) és un truc per comptar: cada hit suma 1, tota la resta suma 0. La proporció de hits va del 0.4% entre els jocs de pagament més barats al 10.4% entre els més cars.

Compte: un preu més alt no fa un èxit

La taula mostra que els jocs cars són hits més sovint. No mostra per què. Els estudis grans cobren més i venen més: la mida de l'estudi pot explicar les dues xifres. Dues coses que es mouen juntes no vol dir que una causi l'altra.

On van els límits?

Un límit és el valor on acaba un interval i comença el següent.

Per què < 5 i no <= 5? Mira els preus més habituals:

SELECT price, COUNT(*) FROM games GROUP BY price ORDER BY 2 DESC LIMIT 3;
price count
9.99 3686
4.99 3204
(buit) 3095

Els preus s'amunteguen just per sota del número rodó: 4.99, 9.99. Per això el límit va sobre el número rodó, i tothom està d'acord en quin costat cau 4.99.

Bona pràctica Per què
Posa els límits en números rodons Fàcils de dir, fàcils de recordar, i els preus s'agrupen just per sota
Usa una sola direcció a tot arreu (< a cada branca) Cap valor pot caure en dos intervals ni en cap
Anomena els intervals pels seus límits (5 to 15) El lector veu la regla a l'etiqueta
Dona al NULL el seu propi interval Si no, s'amaga dins de ELSE

Compte: les franges no són a les dades

Tu has dibuixat les línies al 5, al 15 i al 30. Mou una línia i la història canvia. Un interval és una decisió: té un autor i s'ha d'escriure a l'informe.

I un interval llença informació. Dins de 30 or more, un joc a 39.99 i un programari a 595.99 ara són el mateix.

Categories brutes

Les categories de text també s'embruten. Quants editors hi ha?

SELECT COUNT(DISTINCT publisher) AS as_written,
       COUNT(DISTINCT LOWER(publisher)) AS ignoring_case
FROM games;
as_written ignoring_case
13951 13863

LOWER converteix un text en minúscules. Ignorant les majúscules, desapareixen 88 "editors": eren segones grafies d'un que ja existia. Mira'n dos:

SELECT publisher, COUNT(*) FROM games
WHERE LOWER(publisher) IN ('big fish games', 'capcom')
GROUP BY publisher ORDER BY 1;
publisher count
Big FIsh Games 1
Big Fish Games 284
CAPCOM 1
Capcom 33

Per a tu, dos editors. Per a una base de dades, quatre. La mateixa eina els fusiona:

CASE WHEN LOWER(publisher) = 'big fish games' THEN 'Big Fish Games'
     ELSE publisher END AS publisher

Això dona un sol Big Fish Games amb 285 jocs. A la taula de classe no arreglem les 88 grafies: les coneixem, i això va a l'informe. Conèixer un límit i deixar-lo escrit també és una decisió.

Prova-ho: franges i nivells

Obre SQL → SQL Lab, base de dades Dades classe, esquema public. Executa una sentència cada vegada.

Pas 1 — price_band. Executa la consulta de cinc franges sobre games. Hauries de veure cinc files en l'ordre Free, Under 5, 5 to 15, 15 to 30, 30 or more, que sumen 26,685.

Pas 2 — Trenca-la dues vegades. Fes els dos canvis descrits més amunt, un cada vegada. Abans d'arreglar-ne cap, apunta els recomptes i una frase que digui on han anat a parar els jocs.

Pas 3 — Afegeix les dues columnes a la consulta neta. Obre games_step2 a SQL → Saved Queries, canvia el nom del bloc a step3, i afegeix el teu price_band i l'expressió de owners_tier. Compte amb la coma abans de cada CASE nou. Comprova-ho amb el recompte d'owners_tier: Niche 17,921, Mid 8,141, Hit 623.

Pas 4 — Desa-la. Prem Save, canvia el nom a games_step3, tria Save as new.

Pas 5 — Creua-les. Conserva el bloc WITH step3 i executa la consulta dels hits. Hauries de poder dir quina franja té la proporció de hits més alta i quina la més baixa.

La base de dades no troba la columna price_band

Una columna creada amb AS no es pot fer servir al WHERE ni en una altra columna del mateix SELECT. Només existeix un nivell més amunt, al SELECT que llegeix de step3. Per això la consulta neta viu dins d'un bloc WITH.

Idees clau

  • Una categoria fa llegible un número: 199 preus es converteixen en 5 intervals. El número es queda a la seva pròpia columna, perquè una etiqueta no es pot tornar a convertir en número.
  • CASE WHEN prova les seves branques de dalt a baix i s'atura a la primera certa. L'ordre forma part de la lògica.
  • ELSE agafa tota la resta, inclòs NULL. Anomena primer el cas NULL, amb IS NULL.
  • Un ordre equivocat o una branca que falta no dona cap error: comprova els recomptes de cada franja.
  • Ordena les categories per un número (ORDER BY MIN(price)), no per l'etiqueta, que s'ordena alfabèticament.
  • Els límits són decisió teva. Posa'ls en números rodons, usa una sola direcció i deixa'ls escrits.
  • Les categories de text també s'embruten: Capcom i CAPCOM són dos valors fins que els fusiones.

Comprovació ràpida

1. En un CASE WHEN amb diverses branques, quina branca agafa una fila?
  • L'última branca amb la condició certa
  • La primera branca amb la condició certa, llegint de dalt a baix
  • La branca amb la condició més estreta
  • Totes les branques amb la condició certa

Una fila prova les branques en ordre i s'atura a la primera certa. Per això l'ordre forma part de la lògica: una condició ampla posada primer s'empassa les files pensades per a les branques de sota.

2. Amb CASE WHEN price < 5 THEN 'Under 5' ELSE '5 or more' END, quina etiqueta rep Dota 2 (preu NULL)?
  • Under 5, perquè un joc gratuït costa menys de 5
  • 5 or more, perquè NULL < 5 no és cert i la fila cau a ELSE
  • NULL, perquè el preu és buit
  • La consulta dona un error

Una comparació amb NULL no és mai certa: és desconeguda. La fila no encaixa en cap branca i arriba a ELSE, que agafa tota la resta. Un joc gratuït acaba etiquetat com a car, sense cap error. La solució és una primera branca WHEN price IS NULL THEN 'Free'.

3. Mous WHEN price < 30 THEN '15 to 30' a dalt de tot de la consulta price_band. Què passa?
  • Res: l'ordre de les branques no importa
  • La consulta s'atura amb un error
  • 23,026 jocs reben l'etiqueta 15 to 30, i Under 5 i 5 to 15 desapareixen
  • Només canvien de franja els jocs de més de 30 dòlars

Tot preu per sota de 30 també compleix price < 30, així que totes aquestes files s'aturen a la primera branca. La consulta s'executa i retorna tres franges en lloc de cinc. Una franja que falta al resultat és la pista.

4. Per què el resultat s'ordena amb ORDER BY MIN(price) NULLS FIRST i no amb ORDER BY price_band?
  • Perquè ORDER BY no es pot usar amb una columna de CASE
  • Perquè ordenar per preu és més ràpid
  • Perquè les etiquetes són text i s'ordenen alfabèticament: 15 to 30 aniria abans que 5 to 15
  • Perquè price_band conté valors NULL

Una etiqueta és text, i el text s'ordena com un diccionari: 1 abans de 5. Per obtenir l'ordre lògic ordenes per alguna cosa que encara és un número, com el preu més baix de cada franja.

5. Per què el límit s'escriu price < 5 i no price <= 5?
  • Perquè <= no existeix a PostgreSQL
  • Perquè cap joc no costa exactament 5 dòlars
  • Perquè els preus s'amunteguen just per sota dels números rodons (4.99, 9.99), així que un límit sobre el número rodó els separa netament
  • Perquè < és més ràpid que <=

Els dos preus més habituals són 9.99 i 4.99. Posar cada límit sobre el número rodó, sempre amb <, fa que cada valor caigui en exactament un interval i que tothom estigui d'acord en quin costat és 4.99.

6. COUNT(DISTINCT publisher) dona 13,951 i COUNT(DISTINCT LOWER(publisher)) dona 13,863. Què et diu la diferència?
  • 88 editors no tenen cap joc
  • 88 files tenen l'editor buit
  • 88 valors només són una grafia diferent, en majúscules, d'un editor que ja hi és
  • LOWER ha esborrat 88 files de la taula

LOWER converteix el text en minúscules, així que CAPCOM i Capcom es converteixen en el mateix valor. Els 88 "editors" que falten mai no van ser empreses diferents: són categories brutes, una mateixa cosa escrita de dues maneres.

7. Algú escriu CASE WHEN metascore >= 50 THEN 'Mixed' WHEN metascore >= 75 THEN 'Good' ELSE 'Bad' END. Tria les dues coses que hi ha malament.
  • Un joc amb una puntuació de 90 rep Mixed, perquè >= 50 és cert primer: la branca >= 75 ha d'anar per sobre
  • CASE no es pot usar en una columna numèrica
  • Un joc sense puntuació rep Bad, perquè NULL cau a ELSE: necessita la seva pròpia branca IS NULL
  • Les etiquetes s'han d'ordenar alfabèticament dins del CASE

Les dues regles d'aquesta pàgina alhora. Executa-ho sobre games i cap joc no és Good, mentre que 23,952 són Bad, gairebé tots jocs que simplement mai no han estat valorats. Arreglat (IS NULL primer, després >= 75, després >= 50), la taula té 23,838 sense puntuació, 1,457 Mixed, 1,279 Good i només 114 Bad.

Una de les franges d'avui no era un preu: Free era una suposició sobre què vol dir una cel·la buida. Teníem dret a fer-la? I què passa amb 23,838 puntuacions buides? A continuació: Imputació.