Mi objetivo en este artículo es escribir sobre los usos de la ciencia de datos en la industria de pruebas como ingeniero industrial autodidacta que trabaja para EIIT. Pretende ser un puente para los científicos de datos principiantes o medio utilizados, tanto en el mundo de las TI como en el de la Ingeniería Industrial (IE), que buscan ampliar sus conocimientos de dominio.

Descripción del caso de uso: Pruebas de guía de luz

En el campo de la ingeniería automotriz, el uso de guías de luz como una forma efectiva de producir luz homogénea y difusa en el interior de los vehículos ha aumentado en popularidad en paralelo a los avances en la tecnología LED.

Estos elementos flexibles son poliméricos y transparentes, lo que permite que la luz procedente de un emisor LED rebote y se emita desde toda su longitud.

Emisor de luz equipado con dos guías de luz de Valeo. Fuente

Estos productos se producen en un procedimiento de extrusión que tiene el riesgo de crear malas condiciones de extrusión si los parámetros térmicos de la extrusora son ligeramente incorrectos.

Con el fin de garantizar que las cualidades del color (lo suficientemente blanco) se conserven en los componentes recién producidos, EIIT ha desarrollado una máquina de pruebas bastante simple pero potente. Para cada modelo de producto, se produce un accesorio o «maquinaria de herramientas» para acomodar los productos durante las pruebas.

La prueba se realiza en varios puntos de sensor para cada guía de luz, generalmente tres, conocidos como fibras. Cada fibra mide una coordenada escalar X e Y, por lo que para una guía de luz con 3 fibras habrá 6 medidas (3 x 2). Para simplificar las cosas, cada medida se llama fibra y el número habitual de fibras a estudiar puede variar de 12 a 32.

Machine used for testing. Source: Own work.

In order to ensure the quality of the tests a test must be performed over the test operation (getting a bit redundant here)

This test is known as Reproducibility and Repeatability test (RyR) and ensures that, for a master set of products, an homogeneity is reached for multiple measures. It requires both the measures and the functionality limits imposed by the test:

Donde std es la desviación estándar para cada fibra (fila de 30 valores) y los límites superior e inferior se establecen con el cliente debido a que son relativos. El límite de valor del RyR suele establecerse en el 10%. Superar este número significa que no hay suficiente homogeneidad en las mediciones realizadas.

Extracción y preparación de datos

Como siempre, lo primero para un científico de datos es comprender y procesar los datos sin procesar.

Actualmente, cada ciclo de medición en la máquina se almacena en un único archivo .csv por producto con una serie de valores alfanuméricos. Hay dos versiones de la luminaria, una capaz de contener dos productos (guía de luz izquierda y derecha) y otra capaz de contener cuatro. En el mundo de las pruebas, estas posiciones se denominan nidos. Para el segundo caso, la prueba RyR, que suele constar de 30 compases, producirá 120 informes (30 compases x 4 posiciones). Como puedes adivinar, esto no es lo ideal.

Un único informe visualizado. Fuente: Elaboración propia.

He codificado un programa simple usando una interfaz de usuario de Tkinter que es capaz de extraer los valores necesarios:

  • Nombre de la fibra (NOMBRE)
  • Medición (MEAS)
  • Límite inferior (LO LIM)
  • Límite alto (HI LIM)

Como el código es bastante grande, me limité a agregar las partes más interesantes en mi explicación. Cada posición del producto en un accesorio se denomina nido. Lo primero que hace el programa es cargar los informes, filtrándolos por sus nombres de archivo, ya que cada informe de anidamiento contiene una subcadena en su nombre de archivo que identifica su nido (de S1 a S4).

import tkinter as tk
import tkinter.filedialog
import os
import openpyxl as opxl #Read and write in xlsx
import pandas as pd #Import from csv and manipulate data
import numpy as np

def nest_filter(source_dirname, substring):
    '''Filters each nest reports by reading a substring in the filename'''
    file_list = os.listdir(source_dirname)
    filtered_list = [filename for filename in file_list if substring in filename]
    return filtered_list

A continuación, se genera un ndarray vacío del tamaño deseado para contener los valores:

def ndallocator(nest):
    '''Allocates an empty ndarray of the size needed to hold the values of every nest'''
    size = enumerate(nest)
    size = len(list(size))
    data = np.zeros([len(specific_rows), size])
    return data

The core of the program is a function that writes consecutively the values for that nest and each measure cycle:

def writer(nest, data, source_dirname):
    '''Writes over the array column by column'''
    for i, filename in enumerate(nest):
        Source = pd.read_csv(source_dirname + "/" + nest[i], skiprows = lambda x: x not in specific_rows, header=None) #Open the csv and build a Dataframe with the target rows
        Text = Source.iloc[:, 2] #Indexes the test name column
        MEAS = np.zeros(len(specific_rows))
        for j in range(data.shape[0]):
            try:
                MEAS[j] = float(Source.iloc[j, 3])
            except (ValueError, TypeError):
                MEAS[j] = 0.0
        lo_limit = Source.iloc[:, 4] #Indexes the low limit value
        hi_limit = Source.iloc[:, 5] #Indexes the high limit value
        data[:, i] = MEAS #Writes the column on the array  
    Output = pd.DataFrame(data) #Makes a new Dataframe with the completed array
    Output = pd.concat([Text, Output, lo_limit, hi_limit], axis=1)
    return Output

It ensures that the first column is used for the fiber name, the next thirty are used for the measurements and the last one for the low and high limit.

All of this process is repeated for each nest and then compiled in a single dataframe that is later sent to an empty excel file:

def compiler_4(Output_S1, Output_S2, Output_S3, Output_S4, target):
    '''Concatenates the data frames for each nest'''
    Final = pd.concat([Output_S1, Output_S2, Output_S3, Output_S4])
    Final.to_excel(target, index=False, startrow=3, startcol=0, header=None) #Writes the values in the excel file
    os.startfile(target) #Opens the file for review

This is a rough explanation of what my program does and is intended to summarize the prepossessing of data. There are multiple omitted details for clarity as the possibility of selecting the report read rows for reports with different numbers of fibers and selecting fixtures with different numbers of nests. You can review the full code under in my GitHub repo: https://github.com/LucianoGP95/Python_Eiit_RyR.git

This would be the final output of my program for a 4 nests RyR:

The result of the extraction of data. Source: Own work.

From here, we can use the powerful capabilities of Excel to quickly calcule the RyR value for each row or we can continue our analysis in Python to gain more insight of the data.

Exploratory Data Analysis

Once our data is ready, using exploratory data analysis (EDA) is the first step to achieve an understanding of the data. A Jupyter notebook usage is recommended from this point. A quick scatter plot can be done using.

This machine is an off-line one, meaning the proccess of loading and unloading the test samples is done by hand. This introduces a plethora of problems for a repetitive proccess like the RyR that can generate outliers that don’t represent the real performance of the machine. It is in our interest to pinpoint these issues and minimize them from a data treatment perspective.

First, the values can be plotted together with a custom function that filter them as desired:

#Plots
plot_scatter(df, 'Scatter Plot, all fibers', 'test', 'MEAS', 'Guide: ') #Plot all guides
plot_scatter(df, 'Scatter Plot, fiber X', 'test', 'MEAS', 'Fiber', filter='x') #Plot x axis values
plot_scatter(df, 'Scatter Plot, fiber Y', 'test', 'MEAS', 'Fiber', filter='y') #Plot y axis values

Lo principal aquí es que, como se mencionó anteriormente, el color es medido por el transductor como una característica bidimensional (x, y). Escribí una función de dispersión que filtra cada fila por separado para obtener una mejor información, ya que las diferencias entre los dos ejes tienden a ser notorias.

A continuación, mediante el uso del método describe() podemos echar un vistazo a algunas medidas básicas de estadística descriptiva

resume = df.transpose().describe() #Transpose the df first due to describe() working in columns.
df.transpose().describe() #This second call allows for a nicer output in jupiter.

While every parameter is useful to gain a data insight, the most important one in this case is std as it allows us to calculate the RyR. It’s not hard to write another piece of code that allows us to filter and list the most problematic fibers:

largest = resume.loc['std'].sort_values(ascending=False)[:5]  #Filter the 5 largest values
index = largest.index.tolist()  #Get the index in a list format
for i in range(largest.shape[0]): #Differentiate between x-fibers (odds) and y-fibers
    if index[i] % 2 == 0:
        index[i] = f"fiber x, number {index[i]+1}:"
    else:
        index[i] = f"fiber y, number {index[i]+1}:"
largest.index = index
print("Fibers with largest deviation:")
print(largest)

And we can use the same scatter function to filter fibers by single number to inspect them individually and find those pesky outliers.

plot_scatter(df, 'Scatter Plot, specific fibers', 'test', 'MEAS', 'Fiber', filter=13)

Esta función es bastante engorrosa y no quiero inflar este artículo con código, así que la omití. Como se mencionó anteriormente, todo se puede encontrar en mi repositorio: https://github.com/LucianoGP95/Python_Eiit_RyR.git

Creo que esto es suficiente para envolver esta primera parte del caso de uso. En la siguiente parte hablaré sobre cómo filtrar valores atípicos de una manera matemáticamente correcta y cómo generar nuevos límites para cada fibra.