--- title: Big Data. Intro a Pandas. tags: DAM, python, pandas --- <div style="width: 30%; margin-left: auto;"> ![](https://hackmd.io/_uploads/HJiR4eGJT.png) </div> <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Llicència de Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />Aquesta obra està subjecta a una llicència de <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Reconeixement-CompartirIgual 4.0 Internacional de Creative Commons</a> # BigData. Intro a Pandas. [Pandas](https://pandas.pydata.org/) és una eina d'analisi i manipulació de dades desenvolupada per a ser utilitzada des del llenguatge de programació Python. ## 10 minutes to pandas [10 minutes to pandas](https://pandas.pydata.org/docs/user_guide/10min.html) és el tutorial d'introducció oficial per als no iniciats. ### Importació de biblioteques. Normalment, importarem pandas amb l'alias 'pd' i habitualment també importarem NumPy. ```python= import numpy as np import pandas as pd ``` ### Estructures de dades en pandas. Les estructures bàsiques de dades en pandes són les sèries i els dataframes. Les sèries són vectors unidimensionals i els dataframes són taules bidimensionals. #### Creació d'objectes. Per a més informació veure la [introducció a les estructures de dades](https://pandas.pydata.org/docs/user_guide/dsintro.html#dsintro). Creació d'una sèrie passant-li una llista de dades i deixant que pandas generi un RangeIndex genèric. ```python= #!/bin/usr/python3 import numpy as np import pandas as pd def main(): print("Crea serie a partir d'una llista:") serie = pd.Series([1, 3, 5, np.nan, 6, 8]) print(f"serie =\n{serie}") print("\n" * 2) print("Crea DataFrame a partir de diccionari:") dades = { "Columna A": pd.Timestamp(year=2023, month= 11, day= 20), "Columna B": [12, 24, 36, 48, 60], "Columna C": 1.0, "Altra informació": ["foo", "bar", "foo", "bar", "bar"] } data_frame = pd.DataFrame(dades) print(f" data_frame = \n{data_frame}") print(f"{data_frame.dtypes=}") if __name__ == "__main__": main() ``` #### Llegint dades de fitxers. Pandas ens permet obrir fitxers i crear datasets directament desde les seves dades. Per exemple, podem crear un dataset directament des d'un csv amb la funció 'read_csv': ```python= df = pd.read_csv("./dades/2022_accidents_causa_conductor_gu_bcn_.csv") ``` #### Visualització bàsica de dades. A continuació es mostra un exemple on utilitzem head i tail per mostrar el començament i el final d'un dataframe, mostrem per pantalla les propietats index i columns, utilitzem la funció 'describe' per obtenir informació estadística bàsica de les diferents columnes, ordenem per un dels camps i seleccionem un rang determinat de files. ```python= #!/bin/usr/python3 import numpy as np import pandas as pd def main(): df = pd.read_csv("./dades/2022_accidents_causa_conductor_gu_bcn_.csv") print(df) print("\n"*2) print("head") print(df.head()) print("\n"*2) print("tail --> 2 línies") print(df.tail(2)) print(f"{df.index=}") print(f"{df.columns=}") print(f"{df.describe()}") print("Ordenació") print(f"{df.sort_values(by='Nom_carrer').head(30)}") print("Selecció") print(df["Nom_carrer"]) print("Selecció de rang") print(df[10:13]) if __name__ == "__main__": main() ``` #### Selecció Al exemple anterior hem vist com seleccionar pel nom d'una columna 'df["Nom_carrer"]' i també per un rang de files: 'df[10:13]'. Però la capacitat de selecció de pandas va molt més enllà. Podem realitzar una selecció combinant un rang de files amb una llista de columnes: ```python= #!/bin/usr/python3 import numpy as np import pandas as pd def main(): df = pd.read_csv("./dades/2022_accidents_causa_conductor_gu_bcn_.csv") print(df) print("\n"*2) print("Selecció") print(df["Nom_carrer"]) print("Selecció de rang") print(df[10:13]) print("Selecció combinada de columnes i files") print(df.loc[100:105, ["Nom_districte", "Nom_carrer"]]) if __name__ == "__main__": main() ``` Les seleccions també es poden fer per posició de les files i columnes amb la funció 'iloc'. Per exemple, la comanda: 'df.iloc[300]' per al dataframe que estem tractant, ens donaria la següent sortida: ``` Numero_expedient 2022S007721 Codi_districte 3 Nom_districte Sants-Montjuïc Codi_barri 12 Nom_barri la Marina del Prat Vermell Codi_carrer 701265 Nom_carrer Litoral (Besòs) ... Num_postal 1300000 Descripcio_dia_setmana Divendres NK_Any 2022 Mes_any 12 Nom_mes Desembre Dia_mes 16 Hora_dia 21 Descripcio_causa_mediata Canvi de carril sense precaució Descripcio_torn Tarda Coordenada_UTM_X_ED50 430303.25 Coordenada_UTM_Y_ED50 4579227.37 Longitud 2.165611 Latitud 41.359692 Name: 300, dtype: object ``` En aquest cas no imprimeix la informació en forma de fila, sino que ho fa en format vertical amb el nom de les columnes a la part esquerra i els valors a la part dreta. També podem realitzar seleccións basades en rangs de posicions i en llistes de posicions: ```python= #!/bin/usr/python3 import numpy as np import pandas as pd def main(): df = pd.read_csv("./dades/2022_accidents_causa_conductor_gu_bcn_.csv") print(df.head(2)) print("\n"*2) print("Selecció d'una fila.") print(df.iloc[300]) print("Selecció de rangs de posicions") print(df.iloc[500:503, 8:11]) print("Selecció de llistes de posicions") print(df.iloc[[500, 600, 700], [0, 8, 15]]) if __name__ == "__main__": main() ``` #### Indexació booleana Pandas posa al nostra abast funcions per filtrar per condicions que es puguin avaluar a True o False: ```python= #!/bin/usr/python3 import numpy as np import pandas as pd def main(): df = pd.read_csv("./dades/2022_accidents_causa_conductor_gu_bcn_.csv") print(df.head(2)) print("\n"*2) print("Selecció dels accidents de la segona meitat de l'any.") print(df[df["Mes_any"] > 6]) print("Mostrar accidents del messos de Març i de Novembre") print(df[df["Mes_any"].isin([3, 11])]) if __name__ == "__main__": main() ``` #### Combinar ##### Concat Permet combinar dataframes juntant les files de tots: ```python= >>> df = pd.DataFrame(np.random.randn(10, 4)) >>> print(df) 0 1 2 3 0 0.250334 -0.443551 0.988513 -0.787132 1 -1.465765 0.956211 -0.276900 0.411752 2 -0.177937 -1.197951 0.386362 -0.800747 3 1.116690 2.332147 -1.216588 0.319217 4 0.660634 -0.176374 0.319602 -0.780145 5 0.047625 1.350357 -0.417497 0.825456 6 1.483693 1.294452 1.208015 0.389316 7 -0.505037 0.997385 0.782625 -1.533021 8 0.119392 0.510399 1.607561 -0.003989 9 1.013316 0.752841 -0.120283 1.800011 >>> >>> pieces = [df[:3], df[3:7], df[7:]] >>> print(pieces[0]) 0 1 2 3 0 0.250334 -0.443551 0.988513 -0.787132 1 -1.465765 0.956211 -0.276900 0.411752 2 -0.177937 -1.197951 0.386362 -0.800747 >>> print(pieces[1]) 0 1 2 3 3 1.116690 2.332147 -1.216588 0.319217 4 0.660634 -0.176374 0.319602 -0.780145 5 0.047625 1.350357 -0.417497 0.825456 6 1.483693 1.294452 1.208015 0.389316 >>> ``` ##### Merge. [Més informació](https://pandas.pydata.org/docs/user_guide/merging.html#merging-join). merge() combina per columnes: ```python= >>> left = pd.DataFrame({"key": ["foo", "foo"], "lval": [1, 2]}) >>> right = pd.DataFrame({"key": ["foo", "foo"], "rval": [4, 5]}) >>> print(left) key lval 0 foo 1 1 foo 2 >>> print(right) key rval 0 foo 4 1 foo 5 >>> print(pd.merge(left, right, on="key")) key lval rval 0 foo 1 4 1 foo 1 5 2 foo 2 4 3 foo 2 5 >>> ``` ```python= >>> left = pd.DataFrame({"key": ["foo", "bar"], "lval": [1, 2]}) >>> right = pd.DataFrame({"key": ["foo", "bar"], "rval": [4, 5]}) >>> print(pd.merge(left, right, on="key")) key lval rval 0 foo 1 4 1 bar 2 5 >>> ``` #### Agrupar. [Més informació](https://pandas.pydata.org/docs/user_guide/groupby.html#groupby). El procés d'agrupar implica com a mínim un dels següents processos: * Separar les dades en diferents grups. * Aplicar alguna funció a tots els grups de manera independent. * Combinar els resultats en una única estructura. ```python= >>> df = pd.DataFrame( ... { ... "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"], ... "B": ["one", "one", "two", "three", "two", "two", "one", "three"], ... "C": np.random.randn(8), ... "D": np.random.randn(8), ... }) >>> print (df) A B C D 0 foo one -0.165858 2.053846 1 bar one 0.020635 1.053649 2 foo two -0.761409 0.251071 3 bar three 0.268283 -0.758702 4 foo two 0.318896 -0.195485 5 bar two 0.362911 -1.844101 6 foo one -1.009939 0.325332 7 foo three 1.714997 -1.549752 >>> ``` Agrupació per una única columna: ```python= >>> print(df.groupby("A")[["C", "D"]].sum()) C D A bar 0.651829 -1.549154 foo 0.096687 0.885012 >>> ``` Agrupant per diverses columnes formant un multi-índex: ```python= >>> print(df.groupby(["A", "B"]).sum()) C D A B bar one 0.020635 1.053649 three 0.268283 -0.758702 two 0.362911 -1.844101 foo one -1.175797 2.379178 three 1.714997 -1.549752 two -0.442513 0.055586 >>> ``` ## Format Parket per a fitxers Els fitxers Parquet són un format de emmagatzematge de dades columnar. És un format de fitxer dissenyat per emmagatzemar dades de manera eficient, especialment en entorns de big data, gràcies al seu emmagatzematge columnar, compressió i esquema ric. Un bon lloc per a conèixer millor el format parquet per a fitxers és la [pàgina oficial](https://parquet.apache.org/docs/). Característiques del format parquet: * Emmagatzematge Columnar: En lloc d'emmagatzemar les dades per files, com ho fa un format de fitxer típic com CSV, Parquet emmagatzema les dades per columnes. Això significa que els valors d'una columna es guarden junts en lloc dels valors d'una fila. * Eficiència en Lectura: Aquest enfocament columnar és eficient per a operacions que impliquen llegir només certes columnes de les dades, ja que només s'accedeix a les columnes necessàries, la qual cosa pot reduir el temps de lectura. * Compressió: Parquet també inclou tècniques de compressió, el que significa que les dades s'emmagatzemen de manera més eficient en termes d'espai en disc. * Esquema Ric: Parquet emmagatzema metadades sobre les dades en sí, el que significa que té un esquema ric. Això és útil perquè permet emmagatzemar informació sobre el tipus de dades a cada columna, la qual cosa facilita el procés de lectura i anàlisi. * Optimitzat per a Big Data: Parquet és especialment popular en entorns de processament de dades a gran escala, com Hadoop i Spark. Al ser columnar i comprimit, redueix significativament el temps de lectura i l'espai d'emmagatzematge, la qual cosa és crucial en entorns on es manejen grans quantitats de dades. Fins i tot el '*Ministerio de Asustos Económicos y Transformación Digital*' té publicada [una pàgina](https://datos.gob.es/es/blog/por-que-deberias-de-usar-ficheros-parquet-si-procesas-muchos-datos) on ens insta a utilitzar aquest tipus de fitxers i ens explica el per què. Amb pandas podem escriure directament a un fitxer de tipus parquet amb la funció: ```python= df.to_parquet("./dades/2023_pad_mdbas_edat-1.parquet") ``` I omplir un DataFrame a partir d'un fitxer parquet amb read_parquet: ```python= df = pd.read_parquet('./dades/2023_pad_mdbas_edat-1.parquet') ``` ```