Reemplazo de Matlab con Python - Parte 3: Historia y más información de datos
Estoy compartiendo cómo usé Python para completar la segunda parte de una tarea de ejemplo originalmente realizada con MATLAB. Aprendí más sobre cortar marcos de datos y hacer histogramas con dos bibliotecas diferentes.
Luis Medina
Acostumbrarse a hacer cálculos de ingeniería o científicos utilizando Python puede ser complicado, especialmente si comienza con un fondo de MATLAB. Algunas cosas pueden parecer realmente más fáciles de hacer una vez que comprenda cómo funciona Python, y algunas otras cosas pueden parecer que requieren mucho trabajo adicional para obtener los resultados que desea, en comparación con Matlab. Eso me sucedió, particularmente al crear figuras y trazar datos, luché por obtener exactamente el mismo aspecto que mis figuras de Matlab.
Hablando de replicar lo que hacemos en Matlab, esto es lo que realmente estoy haciendo con esta serie de publicaciones sobre la reemplazo de Matlab con Python . Las últimas entradas se han centrado en una tarea de ejemplo que resolví por primera vez con Matlab , y ahora estoy tratando de hacer lo mismo con Python. Para mantener esta publicación corta, le estoy ahorrando la explicación del contexto y lo que estamos tratando de lograr, ya que ya he hablado de ello en las dos publicaciones anteriores.
Espero que este tipo de ejemplos sea útil si también está aprendiendo Python e intenta hacer cosas similares para la escuela, el trabajo o simplemente el pasatiempo.
En la publicación anterior , completé la primera parte de la tarea, estimando las velocidades del vehículo y los tiempos transcurridos de dos conductores, utilizando algunos datos sin procesar. Vimos diferentes ejemplos de cómo indexar (cortar) los marcos de datos de Pandas rápidamente para resolver nuestros datos. También usé Pyplot y Plotly para crear figuras con subtramas de series de tiempo. Esta vez, te mostraré cómo completé la segunda parte de la tarea. Este será un ejercicio útil para practicar hacer histogramas. Intentaré de nuevo ambas bibliotecas ya que son muy diferentes y quiero aprender ambas.
¡Comencemos!
Creación de histogramas con pyplot
Quiero comparar cuánto tiempo pasan los conductores a diferentes velocidades. Una forma común de hacerlo es haciendo un histograma de sus velocidades.
Al igual que con Matlab, aprendí dos formas diferentes de hacer esto usando las bibliotecas "estándar" para Python. En este caso, estoy llamando a Pyplot "estándar" solo porque es probablemente la primera biblioteca de trazado que la mayoría de las personas ven y comienzan a usar cuando vienen de MATLAB. La sintaxis y la lógica son bastante similares: es parte de matplotlib .
Comenzaré creando una figura con dos subtramas, y usaré cada trama secundaria para ilustrar un enfoque diferente; esto es lo mismo que hice con Matlab aquí , quiero resaltar el paralelismo.
fig, axs = plt.subplots(1,2, sharex=True, figsize = (3000/300, 1000/300), dpi = 300)
fig.suptitle('Different types of histogram using Pyplot (driver speed comparison)', fontweight='bold')
# Define common bins to use
histbins = np.linspace(0, 100, num=20, endpoint=True)
Ahora trazaré en el lado
izquierdo dos histogramas a la vez. Para hacer esto, puse las dos
columnas de DataFrame en una lista usando [<data1>, <data2>]
- Recuerde de las publicaciones anteriores que tenemos nuestros datos almacenados en dos marcos de datos, df1
y df2
. Luego, conociendo el orden de mi lista, puedo definir el formato de manera consistente para cada serie de datos.
# Left side: Side-by-side bars, not normalized axs[0].set_title('Side-by-side bars', loc = 'center') axs[0].grid(alpha= 0.3) axs[0].hist([df1['Vehicle Speed'], df2['Vehicle Speed']], bins = histbins, density=False, histtype='bar', color=['blue', 'red'], label=['Driver 1', 'Driver 2'], edgecolor = 'black') axs[0].set_ylabel('Counts') axs[0].set_xlabel('Vehicle speed [km/h]') axs[0].legend()
Observe que estos dos histogramas no están normalizados ( density = False
) y los recuentos se representan usando barras de lado a lado.
Luego, en el lado derecho,
crearé dos histogramas superpuestos, esta vez normalizados. Aquí es
especialmente importante usar contenedores comunes, de lo contrario, la
comparación estará sesgada. Por eso definí histbins
por adelantado. También es una buena práctica agregar algo de transparencia usando el alpha
argumento para poder ver las áreas donde se superponen.
# Right side: Overlapping and normalized histograms
axs[1].set_title('Overlapping histograms', loc = 'center')
axs[1].grid(alpha= 0.3)
axs[1].hist(df1['Vehicle Speed'], bins = histbins, density=True,
histtype='bar', color='blue',
label='Driver 1', edgecolor = 'black',
alpha = 0.5)
axs[1].hist(df2['Vehicle Speed'], bins = histbins, density=True,
histtype='bar', color='red',
label='Driver 2', edgecolor = 'black',
alpha = 0.5)
axs[1].set_ylabel('Probability Density Function (PDF)')
axs[1].set_xlabel('Vehicle speed [km/h]')
axs[1].legend()
El resultado está muy cerca de la figura de Matlab: 
Como ya hemos discutido, el segundo conductor fue más rápido (pasó más tiempo a velocidades más altas).
Lo siguiente que queremos hacer es averiguar a qué velocidades eran los dos frenados, es decir, a qué velocidad del vehículo estaban comenzando a presionar el pedal del freno en las entradas de la esquina. Esto nos da una mejor idea de lo que los hace tener velocidades tan diferentes, ya que ambos usaban exactamente el mismo automóvil (en diferentes momentos, por supuesto).
Reducir los marcos de datos de pandas para identificar las prensas del pedal de frenos
Necesitamos encontrar las velocidades al comienzo del frenado. Con Matlab, primero intenté hacer esto con un for-bucle (que tomó años) y luego mostré lo más rápido que era usar operaciones de indexación. Aquí, saltaré directamente a la parte de indexación.
Como en el ejemplo de Matlab, primero definí un valor umbral bajo de presión para identificar el inicio de las prensas de pedal.
p_start = 2
Entonces, quiero encontrar las filas que satisfagan estas tres condiciones:
- La presión actual es menor que el umbral
- La presión en el siguiente instante es mayor que el umbral
- El vehículo se mueve por encima de una velocidad mínima
Para comparar los dos instantes, hice copias de las columnas que contenían las presiones de frenos y las cambié por una fila hacia arriba. De esta manera, tengo los valores de presión y los siguientes en las mismas filas. Luego eliminé la última fila de cada marcado de datos, ya que contendría un valor vacío debido al cambio de columna.
df1['BrakesShifted'] = df1['BrakeFront'].shift(-1)
df1 = df1[:-1]
df2['BrakesShifted'] = df2['BrakeFront'].shift(-1)
df2 = df2[:-1]
¿Quizás un truco poco ortodoxo? No lo sé, pero funcionó muy bien. Teniendo eso, solo necesitaba usar el loc
Método para cortar los marcos de datos utilizando las tres condiciones
mencionadas anteriormente y almacene los resultados en un nuevo par de
marcos de datos:
Brake_start1 = df1.loc[(df1.BrakeFront <= p_start) &
(df1.BrakesShifted > p_start) &
(df1['Vehicle Speed'] >= 15) ]
Brake_start2 = df2.loc[(df2.BrakeFront <= p_start) &
(df2.BrakesShifted > p_start) &
(df2['Vehicle Speed'] >= 15) ]
import plotly.graph_objects as go
from plotly.subplots import make_subplots
Lo primero que debe hacer
importado la biblioteca es crear una figura a partir de la biblioteca de
objetos gráficos. Tuve que especificar de antemano que la cifra tendrá
ejes secundarios. Para hacer esto, paso a la función make_subplots
Un argumento que consiste en una lista de diccionarios (en este caso
solo uno, lo que indica que el eje secundario está habilitado). ¿Cómo
sabría eso? Muy simple, lo aprendí de la documentación .
fig = make_subplots(specs=[[{"secondary_y": True}]])
Esto creó una figura, y ahora
puedo agregar rastros para la velocidad del vehículo y la presión del
freno. Quiero superponerlos, usando un eje X común (base de tiempo).
Para hacer esto, agregué dos trazas independientes, una para cada
variable, pero los datos para el eje X son los mismos para ambos.
# Add line plots fig.add_trace( go.Line(y=df1['Vehicle Speed'] ,x=df1.Time_s,name = 'Vehicle Speed'), secondary_y=False) fig.update_yaxes(title_text="Speed [km/h]", range=[0, 80], secondary_y=False) fig.add_trace( go.Line(y=df1['BrakeFront'] ,x=df1.Time_s,name = 'Brakes Pressure'), secondary_y=True) fig.update_yaxes(title_text="Pressure [bar]", range=[0, 50], secondary_y=True, color = 'red')
Observe que especificé la opción secondary_y=True
para el segundo conjunto de datos (en este caso la presión). En Matlab, habría emitido un yyaxis right
Comando justo antes de crear la trama de segunda línea. El procedimiento aquí es simplemente diferente.
A continuación, quería agregar líneas verticales para indicar los instantes que identifiqué como inicios de maniobras de frenado. Si la presión aumentaba repentinamente desde esos puntos, y la velocidad del vehículo era plausible para una maniobra de frenado, entonces los puntos identificados tienen sentido.
Para hacer las líneas, tuve que recurrir a un for-bucle esta vez:
for time in Brake_start1.Time_s: fig.add_shape(go.layout.Shape(type = "line", yref = "y", xref = "x", x0 = time, y0=0, x1 = time, y1 = 100))
Qué vergüenza por no tener un método mejor. Esto no me dejará dormir por la noche hasta que descubra cómo hacerlo de manera más eficiente. O tal vez no, ya veremos.
Para terminar, agregué algún formato con el siguiente código:
fig.update_xaxes(title_text="Time [s]", range = [700, 750] )
fig.update_layout(title = {
'text': '<b>Identified starts of braking maneuvers</b>',
'xanchor' : 'center',
'x' : 0.5, 'y': .95, 'font_size' : 12
},
showlegend=False)
fig
¡El resultado se ve bastante bien! Y la trama interactiva se puede usar en sitios web,
como este 😃
Creación de histogramas con Plotly
¡DE ACUERDO! Los puntos identificados como comienzos de maniobras de frenado tienen sentido. Ahora es el momento de usar Plotly para hacer algunos histogramas.
Creé una figura y definí los contenedores comunes para usar. Observe que los contenedores se definen utilizando un diccionario:
#Create figure
fig = go.Figure()
#Define common bins
bins = dict(start= 10,
end= 100,
size= 5)
Luego, agregué los rastros y formateé la figura:
fig.add_trace(go.Histogram(x=Brake_start1['Vehicle Speed'],
xbins = bins,
histnorm='probability density',
opacity=0.9,
name = 'Driver 1'))
fig.add_trace(go.Histogram(x=Brake_start2['Vehicle Speed'],
xbins = bins,
histnorm='probability density',
opacity=0.7,
name = 'Driver 2'))
fig.update_layout(
title_text='<b>Vehicle speeds at the start of braking</b>',
xaxis_title_text='Vehicle speed [km/h]',
yaxis_title_text='Probability Density',
bargap=0.2,)
fig.show()
¡Los histogramas no mienten! Nuevamente, pero esta vez con Python, podemos ver que el primer conductor estaba frenando antes que el segundo. Aunque ambos alcanzaron velocidades de 80 km/h, parece que el primer conductor nunca hizo una maniobra de frenado por encima de 75 km/h. En cambio, el conductor 1 hizo un número significativamente mayor de maniobras de frenado entre 65 y 70 km/h; el pico para el conductor 2 es de alrededor de 75 - 80 km/h en su lugar.
Conclusión
Solo así, hemos completado la última parte de la publicación original que escribí sobre hacer esto con Matlab . Este fue un análisis muy simplificado o superficial, pero creo que fue un gran ejemplo de juguete para mí practicar y aprender más Python con algunas tareas específicas. Tuve que filtrar datos, indexar los marcos de datos, trazar datos de series de tiempo y crear diferentes tipos de histogramas. Incluso usé figuras con subtramas y ejes secundarios.
Estoy empezando a adoptar el hecho de que Python está destinado a usarse de una manera diferente, y las cosas son realmente más fáciles ahora que me concentro en aprender cómo usar el lenguaje para completar una tarea, en lugar de replicar exactamente lo que hice usando Matlab, especialmente cuando se trata de trazar datos. Si obtengo el mismo aspecto, entonces está bien. Si no lo hago, pero los resultados son decentes, no me enfatizo demasiado por el formato, a menos que sea estrictamente necesario.
Realmente disfruté haciendo esta serie de publicaciones, y espero que hayas encontrado algunas cosas útiles aquí. De todos modos, el reemplazo de Matlab con la serie Python no termina aquí, solo estoy haciendo algo de espacio para otros artículos que he preparado, y ciertamente agregaré otras publicaciones para esta serie en el futuro. Quizás más relacionado con el tema original de este blog, que en realidad está haciendo cosas. Ya veremos.
¡Gracias por leer esto y estad atentos para el próximo!
¡Salud!
La forma pitónica de hacer las cosas
Una de las cosas más populares de Python es su legibilidad. Cuando el idioma se usa correctamente, el código es más eficiente y casi se lee como inglés simple. La mejor manera de lograr esto es seguir un conjunto de pautas para el estilo de codificación, que se conocen como la "pitónica" . Intentaré aprender esto y aplicarlo a mi código a medida que avanza. Creo que te hace pensar más en términos de eficiencia del código y hacer un mejor uso del idioma.
No hay comentarios.:
Publicar un comentario