2. Manipulació de Dades amb Numpy
1. Introducció a NumPy
Els conjunts de dades a manipular poden tindre diferents naturaleses i formats, incloent documents, imatges, sons, mesures... A pesar d'aquesta aparent heterogeitat, poden ser tractats com a conjunt d'arrays numèrics.
Per exemple, una imatge pot ser representada com un array bidimensional de números, on cada element representa el color del pixel que ocupa a l'array. El so pot ser representat com un array unidimensional, on cada element representa la frqüència front al temps. El text pot ser convertit a representació numèrica mitjançant UTF-8. No importa la naturalesa de les dades, el primer pas en l'anàlisi de dades serà convertir aquestes dades en números.
És per això que un emmagatzematge i manipulació eficient de les dades és absolutament fonamental en el procés d'anàlisi de dades, que ens permetran la construcció de programes d'Intel·ligència Artificial.
Ací és on entra en joc NumPy (Numerical Python), que ens ajudarà a carregar, manipular i guardar dades en format numèric. Un array de NumPy serà semblant a una llista (List) de Python, però de naturalesa homogènia i numèrica.
NumPy és molt més eficient en el tractament de dades, sobretot quan la quantitat d'informació creix. Representa el nucli de l'ecosistema de ciència de dades amb Python. Aprendre'l serà beneficiós siga quin siga el camp d'interés.
Per a instal·lar NumPy, simplement heu d'executar l'ordre:
| Bash | |
|---|---|
Amb la següent ordre podem comprovar si s'ha instal·lat correctament i quina és la versió que tenim instal·lada.
Observareu que la majoria de gent importa utilitzant l'àlias np:
| Python | |
|---|---|
1.1. Numpy array vs Python List
En anteriors unitats, es va explicar que Python és un llenguatge de tipat dinàmic, per això permet la creació de llistes heterogènies:
| Python | |
|---|---|
Però aquesta flexibilitat ve acompanyada d'un cost: cada element ha d'acompanyar-se del seu tipus, la seua posició en memòria i altra informació. Cada element és en realitat un objecte.
En canvi, si els elements son homogenis, molta d'aquesta informació seria redundant. Per això és molt més eficient aquest tipus de dades, tal com es mostra a la següent imatge:

La llista conté un punter a un bloc de punters, que apunten a cada objecte, mentre que NumPy apunta simplement a un bloc que conté les dades. És per això que és menys flexible però molt més eficient.
1.2. NumPy Arrays
Creació d'arrays a partir de llistes
| Python | |
|---|---|
Creació d'arrays des de zero
Mostra aleatòria seguint una distribució normal o de Gauss
| Python | |
|---|---|
2. Manipulació d'arrays de NumPy
2.1. Atributs dels arrays
Dimensions
| Python | |
|---|---|
forma: (3, 4, 5)
tamany: 60
Tipus i espai ocupat per les dades
| Python | |
|---|---|
tipus de dades: int64
tamany d'element: 8 bytes
tamany total: 480 bytes
2.2. Accés
Accés als elements d'un array
[5 3 2 6 9 3]6
9
| Python | |
|---|---|
[1 5 2 7]
[4 3 4 6]]
4
6 [[12 8 7 7]
[ 1 5 2 7]
[ 4 3 4 6]]
Pregunta
Com quedarà la variable array després de la següent assignació?
| Python | |
|---|---|
Comprovar resposta
[[3 8 7 7]
[ 1 5 2 7]
[ 4 3 4 6]]
Comprovar resposta
[[3 8 7 7]
[ 3 3 3 3]
[ 4 3 4 6]]
2.3. Subarrays
2.3.1. Unidimensionals
La forma general d'accedir a subarrays és:
| Python | |
|---|---|
principi=0, fi=tamany de la dimensió, pas=1.
Accedint a subarrays en arrays unidimensionals
Compte amb passos negatius
| Python | |
|---|---|
Pregunta
Què contindrà la variable subarray al següent fragment de codi?
Comprovar resposta
| Python | |
|---|---|
2.3.2. Multidimensionals
Accedint a arrays multidimensionals
Accés per files abreviat
| Python | |
|---|---|
Subarrays com a vistes, no com a còpies
Una cosa a tindre molt en compte és que l'accés a un subarray no torna una còpia del mateix sinó una vista. És diferent en l'accés a llistes de Python, on les subllistes si que són còpies de l'objecte original.
Açò implica que treballant amb grans agrupacions de dades, nosaltres podem accedir i processar un subconjunt sense necessitat de copiar les dades.
2.3.3. Còpies d'arrays
En cas que vulgam crear una còpia d'un array, podem fer ús del mètode copy().
Còpia d'un subarray
2.4. Canvi de dimensions
Una altra operació molt útil sobre arrays és el canvi de dimensions.
Canvi de dimensions
2.5. Unió i separació
Per a unir arrays, s'utilitzen les funcions concatenate, vstack i hstack.
Unió d'arrays
De forma similar, per a partir arrays utilitzem les funcions split, hsplit i vsplit.
Partició d'arrays
3. Funcions universals (ufuncs)
La implementació predeterminada de Python (coneguda com CPython) fa algunes operacions molt lentament. Aquesta lentitud es fa evident en situacions en què es repeteix una operació sobre els elements d'una matriu. La lentitud no ve proporcionada per les operacions, sinó per les comprovacions de tipus (recordeu que estem davant un llenguatge interpretat i amb tipus dinàmics) i les crides a funcions en cada iteració.
La computació de matrius NumPy pot ser molt ràpida o molt lenta. La clau per fer-ho ràpid és utilitzar operacions vectoritzades, generalment implementades mitjançant les funcions universals de NumPy (ufuncs). Les funcions universals fan que s'executen de forma compilada totes aquestes operacions, de forma que la velocitat d'execució és rapidíssima.
Operacions universals
| Python | |
|---|---|
Realment, aquestes operacions fan la crida a les versions compilades de Numpy add, subtract, divide...
4. Agregacions
La majoria de vegades, davant d'una gran quantitat de dades, el primer pas és calcular estadístiques de resum de les dades en qüestió. Habituals s'utilitzen la mitjana i la desviació estàndard, que permeten resumir els valors "típics" d'un conjunt de dades, però també són útils altres agregats (la suma, el producte, la mediana, el mínim i el màxim...).
NumPy té funcions d'agregació incorporades ràpides per treballar amb matrius. Recordeu sempre utilitzar estes funcions universals front a les funcions de Python estàndar.
La següent taula mostra les principals funcions d'agregació:
| Nom de la funció | Descripció |
|---|---|
| np.sum | Calcular la suma d'elements |
| np.prod | Calcula el producte dels elements |
| np.mean | Calcular la mitjana dels elements |
| np.std | Calcular la desviació estàndard |
| np.var | Calcular la variància |
| np.min | Trobeu el valor mínim |
| np.max | Trobeu el valor màxim |
| np.argmin | Trobeu l'índex de valor mínim |
| np.argmax | Trobeu l'índex de valor màxim |
| np.median | Calcular la mediana dels elements |
| np.percentile | Calcular estadístiques d'elements basades en el rang |
| np.any | Avaluar si algun element és cert |
| np.all | Avaluar si tots els elements són certs |
Les agregacions poden ser al llarg d'una fila o columna:
Agregacions a través de fila o columna
| Python | |
|---|---|
5. Càlcul en matrius: difusió
La difusió és un conjunt de regles per aplicar funcions universals (per exemple, sumes, restes, multiplicacions, etc.) a matrius de diferents mides.
Agregacions a través de fila o columna
| Python | |
|---|---|

6. Comparacions, màscares i lògica booleana
L'emmascarament s'utilitza quan voleu extreure, modificar, comptar o manipular valors d'una matriu en funció d'algun criteri: per exemple, és possible que vulgueu comptar tots els valors superiors a un determinat valor, o eliminar tots els valors atípics que estan per sobre d'algun valor llindar.
6.1. Exploració de dades
Imagineu que tenim una sèrie de dades que representen la quantitat de precipitació de cada dia durant un any en una ciutat determinada.
Si representem l'histograma de la quantitat de precipitacions podem observar la quantitat de dies que no va ploure, la quantitat de dies que va ploure poc,...
Precipitacions a la ciutat de Seattle l'any 2014
| Python | |
|---|---|
L'histograma dona una idea general de les dades, però no ens respon a preguntes com: - Quants dies de pluja hi havia a l'any? - Quina és la precipitació mitjana en aquests dies de pluja? - Quants dies hi va haver amb més de la mitjana de pluja?
Algú podria pensar que amb funcions que recorreguen les dades i comptadors que es van incrementant segons es cumplisquen unes condicions ho podriem averiguar. Però això seria molt ineficient per les raons que hem comentat en apartats anteriors. Ací és on entren les comparacions, les màscares i la llògica de Boole.
6.2. Comparacions
NumPy implementa operadors de comparació com ara <(menor que) i >(més gran que) com a ufuncs per elements. El resultat d'aquests operadors de comparació és sempre una matriu amb un tipus de dades booleà.
Comparacions en NumPy
6.3. Matrius booleanes
6.3.1. Recompte
Una forma d'obtindre quants valors complixen un criteri seria:
Recompte
Podem utilitzar les funcions all i any per veure si tots o algun dels elements complixen certa condició.
Recompte
Les operacions anteriors es poden combinar amb els operadors lògics &(and), |(or), ^(xor) i ~(not).
7. Arrays boolean com a màscares
Per a emmascarar certs valors, utilitzarem els arrays booleans de forma que sols obtindrem els valors que tornen True:
Emmascarament
Torna una matriu unidimensional amb els valors que complixen la condició.Ara podriem obtindre dades com: - la mitja de precipitacions dels dies que ha plogut. - el dia d'estiu amb més precipitacions - el dia, que no siga d'estiu, amb menys precipitacions
Combinació d'operacions
Combinant operacions booleanes, amb l'emmascarament i les funcions d'agregació, podem respondre a la majoria de les preguntes que ens poden interessar sobre les dades.
8. Fancy Indexing
A principi de l'apartat vegerem com accedir a elements d'un array de NumPy. Ara anem a estudiar una forma diferent d'accedir, el que s'anomena el Fancy Indexing, que pot aportar avantatges segons casos. La idea és molt simple, passar una matriu d'index per a accedir als element d'una matriu.
Exemple de fancy indexing
Dimensions de la matriu accedida
Quan s'utilitza fancy indexing, la forma del resultat reflecteix la forma de les matrius d'índex en lloc de la forma de la matriu que s'està indexant o accedint.
Funciona també amb matrius multidimensionals:
fancy indexing a matrius multidimensionals
8.1. Indexació combinada
Podem combinar fancy indexing amb la selecció simple, amb la selecció de subarrays o amb màscares:
Exemple
8.2. Exemple de selecció de valors aleatoris
Un dels usos més comuns del fancy indexing és la selecció de files aleatòries d'una matriu.
Imagineu una distribució com la següent:
| Python | |
|---|---|
Podem utilitzar la fancy indexing per a seleccionar 20 punts aleatòris.
Si ho representem en un gràfic de punts:
8.3. Modificant valors amb fancy indexing
Amés de poder accedir de forma còmoda utilitzant el fancy indexing també podem modificar valors a través d'este mecanisme.
Exemple
Pregunta
Quin serà el resultat d'executar el següent codi?
Resposta
| Python | |
|---|---|
Tingues en compte que x[i] -= 10 substituix a x[i] = x[i] - 10, executat sobre cadascun dels índex.
Pregunta
Quin serà el resultat d'executar el següent fragment de codi?
Resposta
| Python | |
|---|---|
Fixeu-se de nou, que l'assignació de nou s'executa dues vegades sobre el mateix índes, per tant, la segona elimina l'efecte de la primera i l'element de la posició zero serà 6.
Pregunta
Quin serà el resultat d'executar el següent fragment de codi?
Resposta
| Python | |
|---|---|
En este cas el resultat no és [ 6., 0., 1., 2., 3., 0., 0., 0., 0., 0.] com s'esperaria, i això és perquè l'expressió x[i] + 1 és avaluada a 1 al principi, i l'assignació x[i] = x[i] + 1 es fa diverses vegades, sense incrementar el valor.
Si voleu incrementar utilitzant el fancy indexing podeu utilitzar les funcions at().
8.4. Creació de dades aleattòries amb el fancy indexing
Imagineu que volem agrupar una sèrie de valors en rangs definits per crear un histograma. Ho podem fer de la següent forma:
Obtenim el mateix resultat si directament utilitzem la funció de matplotlib hist() que fa un tractament similar al que hem fet nosaltres manualment.
| Python | |
|---|---|
9. Ordenació d'arrays
Pregunta
Què fa el següent algorisme que rep com a entrada un array?
Resposta
L'anterior algorisme és l'algorisme d'ordenació per selecció. En cada passada selecciona el mínim valor dels que queden a la dreta de l'actual i l'intercanvia. Té un cost de N^2. Requerix que per cada valor és recorrega l'array, un per a ordenar i l'altre per a trobar el mínim valor. Si creix molt el número d'elements, el cost computacional seria elevadíssim.
Pregunta
Què fa el següent algorisme que rep com a entrada un array? Serà computacionalment més o menys costós que l'anterior?
Resposta
L'anterior algorisme és l'algorisme d'ordenació per barreja aleatòria. El seu cost és NxN! (N per N factorial). El factorial és de les funcions amb més ràpid creiximent. És tan costós que no s'hauria d'utilitzar mai en aplicacions reals.
9.1. np.sort i np.argsort
Afortunamdament tant python com numpy tenen algorismes d'ordenació molt més optimitzats que els anteriors.
| Python | |
|---|---|
9.2. Ordenació per files o columnes
Una característica útil dels algorismes d'ordenació de NumPy és la capacitat d'ordenar al llarg de files o columnes específiques d'una matriu multidimensional mitjançant l'argument axis.
Exemple
9.3. Ordenació parcial np.partition
De vegades ens pot interessar no ordenar tot un array sinó trobar els k valors més menuts d'una matriu, encara que no s'ordene. Per a fer això podem utilitzar la funció np.partition. Esta funció rep un array i un número k. El resultat és un nou array amb els k números més menuts a l'esquerra de la partició i la part dreta amb la resta de valors. L'ordenació és arbitrària, és a dir, ninguna de les dos parts està ordenada, però estan els k més menuts a l'esquerra i després la resta.
També podem utilitzar esta funció a través d'un eix en una matriu multidimensional: