Pandas es una librería de Python pensada para manejar datos con la idea de que estos están en un formato de tabla. Es muy usada en análisis de grandes cantidades de datos o de series de valores que tengan una evolución temporal. Es un reemplazo muy eficiente de las planillas de cálculo. En el caso particular de la Astronomía, la librería AstroPy tiene integrada internamente un manejo de tablas similar a Pandas.
Para usar Pandas primero importamos la libreria como se hace siempre con la orden import y en este caso sería:
import pandas as pd
Es muy común que se lo importe con el nombre pd
Un DataFrame is una variable de Pandas a la cual le puedo cargar toda una tabla, incluyendo el nombre de las columnas y el nombre de la filas. Lo podría hacer de esta manera:
pd.DataFrame({'Blanco': [22, 61], 'Negro': [148, 2], 'Rojo': [31,10]})
En este comando ya se pueden apreciar varias cosas que caracterizan al uso de Pandas:
- Pandas está diseñado para manejar tablas.
- Estas son de cualquier tamaño, incluso de un tamaño apreciablemente grande.
- Los datos en Panda tienen una estructura que describiremos a continuación, pero que conserva todoas las propiedades de la tabla original.
Ene le ejmeplo es importante notar que "Blanco", "Negro" y "Rojo" son nombres de la columnas, mientras que al no indicar nombre de las filas estas tienen un número de índice que comienza a contarse en "0"
Las tablas no se limitan sólo a números pueden contener cualquier variable básica de Python. Por ejemplo, textos:
pd.DataFrame({'Roberto': ['Me gustó', 'Espantoso'], 'Ana': ['Muy Bueno', 'Regular']})
Aparte de la estructura tipo diccionario que me sirve para cargar las columnas y darles nombre, también como se indicó anteriormente puedo darle nombre a las filas. Estos nombres de las filas se consideran como los índices de la tabla y por ello se los declara con el comando index. Este se usa como en el ejemplo siguiente:
pd.DataFrame({'Roberto': ['Me gustó', 'Espantoso'],
'Ana': ['Muy Bueno', 'Regular']},
index=['Producto A', 'Producto B'])
Las series en comparación con los DataFrames son sólo una secuencia de valores, es dedir, una lista. En cierta manera vuelven a representar las variables "listas" del Python original, pero como veremos más adelante, son bastante más sofisticadas que las originales.
pd.Series([1, 2, 3, 4, 5])
Una serie en Panda se puede considerar como una columna individual de un DataFrame, entonces también tiene asignado un índice. Por ejemplo:
pd.Series([30, 35, 40], index=['2015 Ventas', '2016 Ventas', '2017 ventas'], name='Producto A')
La gran ventaja de Pandas es la lectura de tablas enteras conservando los nombres de columnas e índices. Se pueden leer archivos en ascii, de planillas de cálculo y que se están volviendo muy comunes que son los CSV. Estos son archivos donde los datos están separados por comas, de ahí el nombre: CSV o "Comma Separated Values"
El comando para leer archivos es pd.read_csv en caso de un archivo CSV
Es decir se lo usaría de la siguente forma:
NGC2366_datos = pd.read_csv("NGC2366_all_corregido.csv")
Donde ahora el archivo se asigna en la variable (es decir un DataFrame) en este caso en particular en NGC2366_datos
Notar que no indiqué el tamaño del archivo, por lo cual es podría haber sido muy grande.
Si se quiere ver que tan grande es la tabla que se cargó en memoria, puedo preguntarlo con el comando:
NGC2366_datos.shape
Que indica que el archivo tiene 635514 filas y 7 columnas.
Pero si quiero ver el nombre de las columnas, tengo que verlo preguntando por el encabezamiento es decir:
NGC2366_datos.head()
También se puede indicar al leer que cierta columna debe ser usada como índice, con el argumento "index_col"
NGC2366_datos = pd.read_csv("NGC2366_all_corregido.csv", index_col=0)
NGC2366_datos.head()
Y si necesitara salvar al disco un tabla en particular, lo puedo hacer con el comando:
NGC2366_datos.to_csv('nombre del archivo')
El formato CSV es uno de los posibles, Pandas soporta muchos más (xlsx, json, zip, txt, xml, html, pdf, docs, etc), incluyendo el hecho que cargando otras librerías puede acceder a leer lenguajes tipo SQL de Bases de datos.
Para realizar ciertas tares, Pandas me permite extraer datos de una columna individual, accediendo a ella a través de nombre, por ejemplo:
F555W = NGC2366_datos['F555W']
F814W = NGC2366_datos['F814W']
print(F555W,type(F555W))
Pudiendo de esta manera realizar operaciones entre los datos extraidos. Y luego puedo agregar mis resultados a la tabla pandas, como una nueva columna.
Color = F555W - F814W
NGC2366_datos['Color'] = Color
NGC2366_datos.head()
NGC2366_datos.describe()
Incluso puedo pedir una evaluación de parámetros estadísticos tipo promedio, dispersión, máximos y míninos, etc, en un sólo comando
NGC2366_datos.F555W.describe()
O pedir exactamente el análisis de una columna en particular, por ejemplo el promedio, y lo hacemos indicando la columna por su nombre.
NGC2366_datos.F555W.mean()
Cuyo resultado puedo asignarlo a una variable particular
F555W_mean=NGC2366_datos.F555W.mean()
print(F555W_mean)
Se Puede hacer cálculos en Pandas, pero si quisiera aprovechar la potencia y la versatilidad de NumPy, tendría que extraer los datos pandaa y convertirlos a arreglos Numpy. Y esta tarea es muy sencilla.
Para esto utilizo la función array( ) de NumPy.
import numpy as np
Color= np.array(NGC2366_datos['Color'])
print("Pandas")
print(NGC2366_datos['Color'])
print("")
print("NumPy")
print(Color)
Como vemos entonces para Pandas tengo una tabla y para NumPy un arreglo.
NGC2366_datos.plot.scatter(x='Color',y='F555W',alpha=0.25,figsize=(4,4)).invert_yaxis();
El método o función que agrego al final .invert_yaxis() es para que el eje F555W quede en una forma más conveniente para el uso astronómico, ya que menores valores de este filtro indican mayor brillo de la estrella.
Si lo hubiera realizado usando matplolib también hubiese sido posible, aunque es un poco más laborioso. Veámoslo:
import matplotlib.pyplot as plt
plt.figure(figsize=(4,6))
plt.plot(NGC2366_datos['Color'], NGC2366_datos['F555W'], ".b",alpha=0.25)
plt.xlim([-3,5])
plt.ylim([30,18]) #note que por razones de interpretación invertí el eje al igual que el gráfico anterior
plt.title("Diagrama color magnitud")
plt.xlabel("Color")
plt.ylabel("Magnitud F555W")
plt.show()
Pandas es muy útil para estudiar series temporales, es decir eventos que van sucediendo durante cierto intervalo de tiempo los cuales por ejemplo, tengo registrados en un archivo. Para ver como funciona Pandas con este tipo de situación generaremos una serie temporal con números al azar. Para hacerla más interesante crearemos una segunda serie, pero ahora de los valores de la primera acumulados. Es decir, el primer elemento es el primero de la segunda, pero el segundo elemento es primero mas el segundo y así continanos hasta el final.
Resumiendo:
ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))
ts.plot();
Como se le indicó periodo 1000 mil días, tenemos simulados un poco menos de 3 años de datos.
Genero la serie acumulada y para ahorrar memoria utilizo la misma variable.
ts=ts.cumsum()
ts.plot();
Describo sus propiedades
ts.describe()
O en vez de una serie puedo crear varias que compartan el eje temporal y las guardo en un dataframe entero. Y todo esto en muy pocas órdenes:
df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index, columns=list("ABCD"))
df = df.cumsum()
df.plot();
Pero si me interesa graficar sólo uno de las curvas, la pido por su nombre:
df['B'].plot();
O puedo graficar de a dos, indicando cuales quiero como en el ejemplo anterior
df['A'].plot()
df['C'].plot();
O graficar los puntos de 'A' contra los B, sin que se unan estos puntos con líneas (scatter plot in inglés). Alpha es la semi tranparencia, para que se vea la sobreposición de puntos.
df.plot.scatter(x='A',y='B', alpha=0.5);
También puedo hacer que se dibuje un gráfico tipo box, donde se indica el valor medio y el 20% de todos los puntos, indicando con negro los puntos "outliers" (que son los que estadísticamente no parecen pertenecer a la muestra).
df.plot.box();
df=df.abs() # hago esto para que todos los valores sean positivos
df.plot.area(figsize=(12,4),subplots=True);
Y podemos también hacer histogramas indicando el número de intervalos (el default es 10), pero acá pedimos que sean 20 intervalos (o "bins" en inglés)
df['A'].plot.hist(bins=20);
Pero también puedo hacerlo para todos los grupos al mismo tiempo:
df.plot.hist(bins=20, alpha=0.5);