Datasets i destí¶
Exercici datasets-and-destination-annex.pdf · PDF
A la lliçó anterior vas unir pokemon amb generations, amb les teves pròpies mans. On és ara aquella consulta?
Busca-la. En el millor dels casos és en algun lloc de SQL → Query History. El més probable és que hagi desaparegut, i la setmana que ve la tornaràs a escriure de memòria, i et sortirà una mica diferent. Una consulta que has de tornar a escriure és una consulta que la propera vegada faràs malament. Aquesta lliçó et dona dues maneres de guardar la teva feina. Una guarda la pregunta. L'altra guarda la resposta. No són el mateix, i triar la que no toca és com un informe acaba ensenyant les xifres del mes passat.
Què aprendràs
- Distingir un dataset físic d'un dataset virtual a Superset
- Desar una consulta de SQL Lab com a dataset virtual
- Escriure el resultat d'una consulta a la base de dades com a taula nova amb
CREATE TABLE ... AS - Predir quin dels dos es queda desfasat quan canvia l'origen, i triar entre ells
- Portar dades d'una API a Superset: JSON → script → CSV → càrrega
- Desactivar la trampa del dia: una taula creada a partir d'una consulta no continua connectada a aquesta consulta
Recepta o carmanyola¶
Fas una lasanya boníssima. Com t'assegures de poder-la tornar a menjar d'aquí a un mes?
Dues respostes. Apuntar la recepta. O cuinar-ne una bona tanda i guardar-la en una carmanyola a la nevera. Una analogia per a tota aquesta pàgina: la base de dades és una cuina, i les taules de PostgreSQL són els ingredients del rebost.
| Recepta | Carmanyola | |
|---|---|---|
| Què guardes | La pregunta (el SQL) | La resposta (les files) |
| A Superset | Un dataset virtual | Una taula nova, feta amb CREATE TABLE ... AS |
| On viu | A Superset: només el text de la consulta | A PostgreSQL: una taula real, al costat de pokemon |
| Quan l'uses | Es torna a cuinar cada vegada | Se serveix tal com es va guardar |
| Per tant és | Sempre fresca, una mica més lenta | Ràpida, però tan vella com el dia que la vas cuinar |
flowchart TB
Q["<b>La teva consulta</b><br>pokemon JOIN generations"]
Q -- "guardes la pregunta" --> V["<b>Dataset virtual</b><br>(la recepta)<br>desat a Superset com a text"]
Q -- "guardes la resposta" --> T["<b>Taula nova</b><br>(la carmanyola)<br>desada a PostgreSQL com a files"]
V -- "torna a executar la consulta<br>cada vegada" --> C["Gràfics"]
T -- "llegeix les files desades" --> C
En el joc de crafting d'aquest mòdul, aquest és el moment en què deixes de portar el botí a les mans i el poses al cofre de la teva base. Les dades que només existeixen a la pantalla encara no estan extretes. Han d'arribar a algun lloc: el seu destí.
Datasets: de què pot dibuixar Superset¶
Un dataset és el que Superset anomena "alguna cosa a partir de la qual puc construir gràfics".
Superset no dibuixa gràfics directament d'una taula ni d'una consulta. Els dibuixa a partir d'un dataset, i n'hi ha de dos tipus:
| Tipus | Què és | A la cuina |
|---|---|---|
| Dataset físic | Apunta a una taula que existeix a la base de dades | Una etiqueta en un pot del rebost |
| Dataset virtual | Una consulta desada que Superset executa per tu | Una fitxa de recepta |
Obre Datasets al menú superior i mira la columna Type. Ja en tens dos: pokemon i generations, tots dos Physical. Superset els va registrar quan vas pujar els fitxers CSV.
Compte: un dataset no és una còpia
Un dataset físic és una etiqueta al pot, no un segon pot. Una taula, zero còpies. Si elimines el dataset, la taula continua a PostgreSQL amb totes les files. I si la taula canvia, el dataset ensenya el canvi, perquè mai no va guardar dades pròpies.
Guardar la pregunta: un dataset virtual¶
Hora d'escriure la primera recepta. Obre SQL → SQL Lab, base de dades Dades classe, esquema public, i executa el join de la lliçó anterior amb les columnes enumerades una per una:
SELECT p.id, p.pokemon, p.type_1, p.type_2, p.hp, p.attack, p.defense, p.speed,
g.generation_id, g.region, g.release_year
FROM pokemon p
JOIN generations g ON p.generation_id = g.generation_id
(JOIN sol vol dir INNER JOIN, i l'AS abans d'un àlies és opcional.) Després desa-la:
| Pas | Fes això | Hauries de veure |
|---|---|---|
| 1 | Executa la consulta i comprova el resultat | 802 files |
| 2 | Clica la fletxa al costat de Save, després Save dataset | Un petit diàleg |
| 3 | Tria Save as new, escriu el nom pokemon_with_region, prem Save & Explore |
El constructor de gràfics s'obre en una pestanya nova del navegador |
| 4 | Tanca aquesta pestanya (els gràfics són la Unitat 3) i obre Datasets | pokemon_with_region a la llista, Type Virtual |
802 files, no 949: és un INNER JOIN, així que les 147 formes alternatives sense generació queden fora. Ja saps per què. Ara aquesta decisió queda escrita en un sol lloc, en comptes de tornar-la a escriure cada vegada.
Compte: tres maneres d'espatllar una recepta
SELECT *en un join. Les dues taules tenen unageneration_id, així que el dataset rep dues columnes amb gairebé el mateix nom i més endavant falla ambcolumn reference "generation_id" is ambiguous. Enumera les columnes, com més amunt.- Un
LIMIToblidat. Superset embolcalla la teva consulta dins la seva. UnLIMIT 10que vas deixar mentre proves es queda a la recepta: aquest dataset tindrà 10 files per sempre. - Un nom que ja està agafat. Dos datasets no poden compartir nom, així que no anomenis el dataset virtual
pokemon.
Guardar la resposta: CREATE TABLE AS¶
CREATE TABLE nom AS SELECT ...executa una consulta una sola vegada i en desa el resultat com a taula nova a la base de dades.
Ara la carmanyola: un resum petit amb una fila per generació. Primer executa només el SELECT (des de la segona línia) i mira el resultat. Després afegeix la primera línia i executa-ho tot:
CREATE TABLE pokemon_by_generation AS
SELECT g.generation_id, g.region, g.release_year,
COUNT(p.id) AS n_pokemon,
ROUND(AVG(p.attack)) AS avg_attack,
MAX(p.speed) AS max_speed
FROM generations g
LEFT JOIN pokemon p ON p.generation_id = g.generation_id
GROUP BY g.generation_id, g.region, g.release_year;
Aquesta vegada no hi ha quadrícula de resultats. SQL Lab diu The query returned no data. No ha fallat res: les files han anat a la base de dades, no a la teva pantalla. Pregunta a la taula nova:
SELECT * FROM pokemon_by_generation ORDER BY generation_id;
| generation_id | region | release_year | n_pokemon | avg_attack | max_speed |
|---|---|---|---|---|---|
| 1 | Kanto | 1996 | 151 | 73 | 150 |
| 2 | Johto | 1999 | 100 | 68 | 130 |
| 3 | Hoenn | 2002 | 135 | 73 | 160 |
| 4 | Sinnoh | 2006 | 107 | 80 | 125 |
| 5 | Unova | 2010 | 156 | 81 | 145 |
| 6 | Kalos | 2013 | 72 | 73 | 126 |
| 7 | Alola | 2016 | 81 | 83 | 151 |
| 8 | Galar | 2019 | 0 |
Vuit files, una per generació. El LEFT JOIN comença des de generations, així que Galar es queda, amb n_pokemon = 0. Els seus avg_attack i max_speed són buits, no 0: no hi ha res per fer-ne la mitjana, i escriure 0 seria mentir.
La taula existeix a PostgreSQL, però Superset encara no té cap dataset per a ella. Posa l'etiqueta al pot:
| Pas | Fes això | Hauries de veure |
|---|---|---|
| 1 | Datasets → + Dataset | El formulari de dataset nou |
| 2 | Base de dades Dades classe, Schema public, Table pokemon_by_generation |
Les columnes de la taula |
| 3 | Clica la fletxa al costat de Create and explore dataset, després Create dataset | pokemon_by_generation a la llista, Type Physical |
Si SQL Lab et respon malament
relation "pokemon_by_generation" already exists: has executat la sentència dues vegades. Bones notícies, no un error per témer: la taula hi és.This database does not allow for DDL/DML: la connexió només deixa llegir a SQL Lab. Això és una configuració de la màquina de classe, no una cosa que hagis fet tu: avisa el teu professor.
Quin es queda desfasat?¶
Aquesta és la idea del dia. Una novena generació de Pokémon va sortir el 18 de novembre de 2022: la regió de Paldea, als jocs Scarlet i Violet. La nostra taula generations no ho sap. Imagina que algú hi afegeix aquesta novena fila.
Abans de continuar llegint, prediu. Quantes files mostrarà la taula pokemon_by_generation? I el mateix SELECT, executat una altra vegada com a consulta?
| Abans de la fila nova | Després de la fila nova | |
|---|---|---|
La carmanyola: la taula pokemon_by_generation |
8 files | 8 files |
La recepta: el SELECT, executat una altra vegada |
8 files | 9 files, Paldea amb 0 Pokémon |
La recepta ha vist l'ingredient nou. La carmanyola, no.
Compte: la taula no està connectada a la seva consulta
"He creat la taula a partir d'una consulta, així que continua enllaçada a la consulta." No. CREATE TABLE ... AS executa la consulta una sola vegada i en desa les files. Una carmanyola no es torna a omplir sola quan compres més tomàquets. Si l'origen canvia, tornes a cuinar: elimina la taula vella amb DROP TABLE pokemon_by_generation; i torna a executar el CREATE TABLE ... AS. Elimina només taules que hagis creat tu. Un dataset virtual no desa res, així que res no se li pot quedar desfasat.
Doncs quan és la carmanyola la tria correcta?
| Tria | Quan |
|---|---|
| Dataset virtual (recepta) | Les xifres han d'estar sempre al dia, i la consulta és prou ràpida |
CREATE TABLE ... AS (carmanyola) |
La consulta és lenta, una altra eina necessita una taula real, o vols conservar com estaven les coses un dia concret |
Una font que no és un fitxer: una API¶
Els teus Pokémon van arribar en un fitxer CSV. Si demà surt un Pokémon nou, ho sabrà el fitxer? No: un fitxer també és una carmanyola. Doncs d'on treu una aplicació la llista fresca?
Una API (Application Programming Interface, interfície de programació d'aplicacions) és una adreça a la qual preguntes i que respon amb dades en comptes d'una pàgina web.
JSON (JavaScript Object Notation) és el format de text en què responen la majoria d'API: noms i valors, on un valor pot contenir més noms i valors a dins.
La PokéAPI és una API gratuïta. Sense compte, sense inici de sessió: l'adreça és tota la pregunta. Obre aquestes dues al navegador (Firefox mostra el JSON com un arbre plegable):
https://pokeapi.co/api/v2/pokemon?limit=20
{ "count": 1351,
"next": "https://pokeapi.co/api/v2/pokemon?offset=20&limit=20",
"previous": null,
"results": [ { "name": "bulbasaur", "url": "https://pokeapi.co/api/v2/pokemon/1/" }, ... ] }
https://pokeapi.co/api/v2/pokemon/charizard
{ "id": 6, "name": "charizard", "height": 17, "weight": 905,
"types": [ { "slot": 1, "type": { "name": "fire" } },
{ "slot": 2, "type": { "name": "flying" } } ],
"stats": [ { "base_stat": 78, "stat": { "name": "hp" } }, ... ] }
(Totes dues respostes estan retallades; count valia 1351 quan es va escriure aquesta pàgina i continua creixent.) Tres coses per llegir-hi:
- La primera resposta diu 1351 però en mostra 20. La resta són a l'adreça
next. Dividir una resposta en pàgines s'anomena paginació. - La part després del
?conté els paràmetres: instruccions addicionals comlimit=20. - Els tipus són una llista dins de la resposta. Les dades dins de dades s'anomenen imbricades.
Ara compara-ho amb la teva taula: SELECT pokemon, height, weight, type_1, type_2 FROM pokemon WHERE pokemon = 'charizard';
| A l'API | A la taula pokemon |
|
|---|---|---|
| Alçada | 17 (decímetres) |
1.7 (metres) |
| Pes | 905 (hectograms) |
90.5 (quilograms) |
| Tipus | Una llista imbricada de dos | Dues cel·les: type_1 = fire, type_2 = flying |
El mateix Pokémon, una altra forma i unes altres unitats. Una font mai no arriba amb la forma que necessites.
Del JSON a una taula: extreure, aplanar, carregar¶
Compte: Superset no es pot connectar a una API
Superset només consulta bases de dades SQL. No pot preguntar a una adreça web. Per tant, una API és una font de la qual has d'extreure i després carregar, exactament com un fitxer CSV.
Aplanar és convertir un JSON imbricat en files i columnes planes.
flowchart LR
A["<b>API</b><br>PokéAPI<br>respon en JSON"] -- "pregunta" --> S["<b>Script</b><br>api_to_csv.py<br>tria i aplana"]
S -- "escriu" --> C["<b>CSV</b><br>pokeapi_sample.csv<br>20 files, 7 columnes"]
C -- "carrega" --> D["<b>Destí</b><br>taula pokeapi_sample<br>a PostgreSQL"]
D --> DS["<b>Dataset físic</b><br>a Superset"]
Aplanar necessita un programa petit. Descarrega api_to_csv.py de la pàgina de Recursos. Són 16 línies de Python:
import csv, json, urllib.request
def get(url):
req = urllib.request.Request(url, headers={"User-Agent": "erd-class"})
return json.load(urllib.request.urlopen(req))
page = get("https://pokeapi.co/api/v2/pokemon?limit=20") # page 1 of the list
with open("pokeapi_sample.csv", "w", newline="") as f:
out = csv.writer(f)
out.writerow(["id", "name", "height_m", "weight_kg", "type_1", "type_2", "hp"])
for item in page["results"]: # each item: a name and a url
p = get(item["url"]) # one request per Pokemon
types = [t["type"]["name"] for t in p["types"]] + [""]
out.writerow([p["id"], p["name"], p["height"] / 10, p["weight"] / 10,
types[0], types[1], p["stats"][0]["base_stat"]])
print(page["count"], "Pokemon in the API. Next page:", page["next"])
En aquest mòdul no has d'escriure Python. Llegeix-lo com a quatre verbs: demanar la llista, demanar cada Pokémon, triar els valors (unitats convertides, tipus separats en dos), escriure una fila.
| Pas | Fes això | Hauries de veure |
|---|---|---|
| 1 | En un terminal, a la carpeta de l'script: python3 api_to_csv.py |
Una línia com 1351 Pokemon in the API. Next page: ... i un fitxer nou pokeapi_sample.csv |
| 2 | Obre el CSV en un editor de text | Una capçalera i 20 files: 6,charizard,1.7,90.5,fire,flying,78 |
| 3 | A Superset: + → Data → Upload CSV to database, base de dades Dades classe, esquema public, nom de taula pokeapi_sample, Upload |
Un missatge de confirmació |
| 4 | A SQL Lab: SELECT type_1, COUNT(*) AS n FROM pokeapi_sample GROUP BY type_1 ORDER BY n DESC; |
bug 6, normal 5, després fire, grass i water amb 3 cadascun |
Ara obre Datasets i mira el Type de pokeapi_sample: Physical. Un cop les dades són a la base de dades, Superset no pot saber que venen d'una API. I és una carmanyola: una instantània, una còpia de l'API tal com era el minut que es va executar l'script. Per refrescar-la, torna a extreure i a carregar.
La llista de comprovació d'una API¶
Sis preguntes per respondre abans de construir res sobre una API, per exemple al teu projecte.
| Pregunta | Què cal esbrinar | PokéAPI |
|---|---|---|
| Adreça | La URL, i els seus paràmetres després del ? |
.../pokemon?limit=20&offset=0 |
| Clau | Necessita un compte o una clau d'API? Una clau és una contrasenya: no la enganxis mai en un xat, un document compartit ni una captura de pantalla | Cap: no cal autenticació |
| Límits | Quantes peticions pots fer? Demana només el que necessites i desa el que reps | Cap límit fix, però la seva política d'ús raonable demana que facis poques peticions |
| Paginació | Una resposta ho conté tot? Quin camp apunta a la pàgina següent? | 20 per pàgina per defecte; next i count |
| Llicència | Pots reutilitzar i publicar les dades? A qui has d'atribuir-ne el mèrit? | Llegeix la seva política d'ús raonable i cita la font |
| Instantània | La teva taula és una còpia d'un moment. Apunta'n la data | Torna a executar l'script per refrescar-la |
Idees clau¶
- Guarda la pregunta o guarda la resposta: un dataset virtual és una consulta desada (la recepta);
CREATE TABLE ... ASdesa les files en una taula nova (la carmanyola). - Un dataset és el que Superset fa servir per construir gràfics. Un dataset físic és una etiqueta sobre una taula, no una còpia.
- Un dataset virtual està sempre fresc: executa la consulta cada vegada. Una taula feta amb
CREATE TABLE ... ASés una instantània i es queda desfasada quan canvia l'origen. CREATE TABLE ... ASno mostra cap quadrícula de resultats: les files han anat a la base de dades. Comprova-ho amb unSELECT.- Una API respon en JSON, sovint imbricat i paginat. Superset no la pot consultar: tu extreus, aplanes a un CSV i carregues.
- Abans d'usar una API, passa la llista de comprovació: adreça, clau, límits, paginació, llicència, instantània.
Comprovació ràpida¶
1. Què és un dataset virtual a Superset?
- Una còpia d'una taula, desada dins de Superset
- Una consulta desada que Superset executa cada vegada que s'usa el dataset
- Una taula creada amb
CREATE TABLE ... AS - Un fitxer CSV que espera ser carregat
Un dataset virtual és la recepta: només es desa el text de la consulta, i es torna a cuinar cada vegada. Una taula feta amb CREATE TABLE ... AS és una taula real, que després registres com a dataset físic.
2. Elimines el dataset físic generations de la llista de Datasets de Superset. Què passa amb la taula de PostgreSQL?
- També s'elimina
- Perd les files però conserva les columnes
- Res: la taula continua allà amb totes les files
- Es converteix en un dataset virtual
Un dataset físic és una etiqueta al pot, no el pot. Una taula, zero còpies: treure l'etiqueta no toca el que hi ha al rebost.
3. Executes un CREATE TABLE ... AS SELECT ... a SQL Lab i no apareix cap quadrícula de resultats. Què vol dir?
- La sentència ha fallat
- La taula s'ha creat buida
- Les files s'han escrit a la base de dades en lloc de mostrar-se en pantalla
- Has de prémer Run una segona vegada
CREATE TABLE ... AS envia el resultat a una taula nova, així que no hi ha res per mostrar. Comprova-ho amb SELECT * FROM la taula nova. Executar-la una segona vegada només respondria already exists.
4. S'afegeix una fila nova a generations. Quina afirmació és certa?
- Tant el dataset virtual com la taula feta amb
CREATE TABLE ... ASla mostren - Cap dels dos la mostra fins que es reinicia Superset
- Una consulta executada una altra vegada veu la fila nova; la taula feta abans amb
CREATE TABLE ... ASno - La taula feta amb
CREATE TABLE ... ASs'actualitza sola, perquè venia d'una consulta
CREATE TABLE ... AS executa la consulta una sola vegada: la carmanyola no es torna a omplir sola. La recepta es torna a cuinar cada vegada, així que veu l'ingredient nou. Per refrescar la taula l'has d'eliminar i tornar-la a crear.
5. Per què Superset no pot mostrar directament les dades de la PokéAPI?
- Perquè la PokéAPI necessita una contrasenya
- Perquè Superset només consulta bases de dades SQL, així que les dades d'una API s'han d'extreure i carregar primer
- Perquè els fitxers JSON són massa grans per a Superset
- Perquè la PokéAPI només funciona a Firefox
Superset pregunta a bases de dades, no a adreces web. El camí és extreure → aplanar → carregar: un script pregunta a l'API, escriu un CSV, i tu puges el CSV com a taula. La PokéAPI no necessita cap clau.
6. La PokéAPI respon amb un count de 1351, però la llista només conté 20 Pokémon. On són els altres?
- Es van eliminar de l'API
- Necessiten una clau d'API
- A les pàgines següents: el camp
nextconté l'adreça de la següent - Al camp
previous
Això és la paginació: una resposta llarga es divideix en pàgines, aquí de 20. count diu quants n'hi ha en total i next apunta a la pàgina següent.
7. El teu cap vol (a) un gràfic de vendes per botiga que estigui sempre al dia i (b) una taula amb els totals de divendres passat que no pot canviar mai. Tria les dues opcions correctes.
- (a) usa un dataset virtual, perquè la consulta s'executa una altra vegada cada cop
- (b) usa
CREATE TABLE ... AS, perquè les files es desen una sola vegada i es queden com estaven - (a) usa
CREATE TABLE ... AS, perquè una taula real és sempre més fresca - (b) usa un dataset virtual, perquè una consulta desada mai no canvia el resultat
Recepta per al fresc, carmanyola per al congelat. Un dataset virtual torna a executar la consulta, així que apareixen les vendes noves. Una taula feta amb CREATE TABLE ... AS és una instantània: exactament el que vols per a "com estaven les coses divendres".
Ja pots portar dades a casa i desar-les, amb Superset. La mateixa feina existeix a totes les eines de dades, amb altres botons. Següent: Obtenir dades amb Power BI, on pots fer la mateixa pregunta: aquesta eina guarda la recepta o la carmanyola?