scikit learn output metrics.classification_report en formato CSV / tab-delimited

Estoy haciendo una clasificación de texto multiclase en Scikit-Learn. El conjunto de datos está siendo entrenado usando el clasificador Multinomial Naive Bayes que tiene cientos de tags. Aquí hay un extracto del script de Scikit Learn para ajustar el modelo MNB

from __future__ import print_function # Read **`file.csv`** into a pandas DataFrame import pandas as pd path = 'data/file.csv' merged = pd.read_csv(path, error_bad_lines=False, low_memory=False) # define X and y using the original DataFrame X = merged.text y = merged.grid # split X and y into training and testing sets; from sklearn.cross_validation import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1) # import and instantiate CountVectorizer from sklearn.feature_extraction.text import CountVectorizer vect = CountVectorizer() # create document-term matrices using CountVectorizer X_train_dtm = vect.fit_transform(X_train) X_test_dtm = vect.transform(X_test) # import and instantiate MultinomialNB from sklearn.naive_bayes import MultinomialNB nb = MultinomialNB() # fit a Multinomial Naive Bayes model nb.fit(X_train_dtm, y_train) # make class predictions y_pred_class = nb.predict(X_test_dtm) # generate classification report from sklearn import metrics print(metrics.classification_report(y_test, y_pred_class)) 

Y una salida simplificada de metrics.classification_report en la pantalla de línea de comandos tiene este aspecto:

  precision recall f1-score support 12 0.84 0.48 0.61 2843 13 0.00 0.00 0.00 69 15 1.00 0.19 0.32 232 16 0.75 0.02 0.05 965 33 1.00 0.04 0.07 155 4 0.59 0.34 0.43 5600 41 0.63 0.49 0.55 6218 42 0.00 0.00 0.00 102 49 0.00 0.00 0.00 11 5 0.90 0.06 0.12 2010 50 0.00 0.00 0.00 5 51 0.96 0.07 0.13 1267 58 1.00 0.01 0.02 180 59 0.37 0.80 0.51 8127 7 0.91 0.05 0.10 579 8 0.50 0.56 0.53 7555 avg/total 0.59 0.48 0.45 35919 

Me preguntaba si habría alguna manera de obtener el resultado del informe en un archivo csv estándar con encabezados de columna regulares

Cuando envío el resultado de la línea de comandos a un archivo csv o trato de copiar / pegar el resultado de la pantalla en una hoja de cálculo: Openoffice Calc o Excel, agrupa los resultados en una columna. Mirando así:

introduzca la descripción de la imagen aquí

Ayuda apreciada. ¡Gracias!

Si quieres las puntuaciones individuales, esto debería hacer el trabajo bien.

 import pandas as pd def classification_report_csv(report): report_data = [] lines = report.split('\n') for line in lines[2:-3]: row = {} row_data = line.split(' ') row['class'] = row_data[0] row['precision'] = float(row_data[1]) row['recall'] = float(row_data[2]) row['f1_score'] = float(row_data[3]) row['support'] = float(row_data[4]) report_data.append(row) dataframe = pd.DataFrame.from_dict(report_data) dataframe.to_csv('classification_report.csv', index = False) report = classification_report(y_true, y_pred) classification_report_csv(report) 

Podemos obtener los valores reales de la función precision_recall_fscore_support y luego colocarlos en marcos de datos. El siguiente código dará el mismo resultado, pero ahora en pandas df :).

 clf_rep = metrics.precision_recall_fscore_support(true, pred) out_dict = { "precision" :clf_rep[0].round(2) ,"recall" : clf_rep[1].round(2) ,"f1-score" : clf_rep[2].round(2) ,"support" : clf_rep[3] } out_df = pd.DataFrame(out_dict, index = nb.classes_) avg_tot = (out_df.apply(lambda x: round(x.mean(), 2) if x.name!="support" else round(x.sum(), 2)).to_frame().T) avg_tot.index = ["avg/total"] out_df = out_df.append(avg_tot) print out_df 

Si bien las respuestas anteriores probablemente funcionen, las encontré un poco detalladas. Lo siguiente almacena los resultados de la clase individual, así como la línea de resumen en un único dataframe. No muy sensible a los cambios en el informe, pero hizo el truco para mí.

 #init snippet and fake data from io import StringIO import re import pandas as pd from sklearn import metrics true_label = [1,1,2,2,3,3] pred_label = [1,2,2,3,3,1] def report_to_df(report): report = re.sub(r" +", " ", report).replace("avg / total", "avg/total").replace("\n ", "\n") report_df = pd.read_csv(StringIO("Classes" + report), sep=' ', index_col=0) return(report_df) #txt report to df report = metrics.classification_report(true_label, pred_label) report_df = report_to_df(report) #store, print, copy... print (report_df) 

Lo que da la salida deseada:

 Classes precision recall f1-score support 1 0.5 0.5 0.5 2 2 0.5 0.5 0.5 2 3 0.5 0.5 0.5 2 avg/total 0.5 0.5 0.5 6 

A partir de scikit-learn v0.20, la forma más sencilla de convertir un informe de clasificación en un dataframe de pandas es simplemente devolviendo el informe como un dict :

 report = classification_report(y_test, y_pred, output_dict=True) 

y luego construir un Dataframe y transponerlo:

 df = pandas.DataFrame(report).transpose() 

A partir de aquí, puede utilizar los métodos estándar de pandas para generar los formatos de salida deseados (CSV, HTML, LaTeX, …).

Consulte también la documentación en https://scikit-learn.org/0.20/modules/generated/sklearn.metrics.classification_report.html

Como se mencionó en uno de los mensajes aquí, precision_recall_fscore_support es análogo a classification_report .

Entonces, es suficiente usar pandas biblioteca de Python para formatear fácilmente los datos en un formato de columnas, similar a lo que hace la classification_report informe. Aquí hay un ejemplo:

 import numpy as np import pandas as pd from sklearn.metrics import classification_report from sklearn.metrics import precision_recall_fscore_support np.random.seed(0) y_true = np.array([0]*400 + [1]*600) y_pred = np.random.randint(2, size=1000) def pandas_classification_report(y_true, y_pred): metrics_summary = precision_recall_fscore_support( y_true=y_true, y_pred=y_pred) avg = list(precision_recall_fscore_support( y_true=y_true, y_pred=y_pred, average='weighted')) metrics_sum_index = ['precision', 'recall', 'f1-score', 'support'] class_report_df = pd.DataFrame( list(metrics_summary), index=metrics_sum_index) support = class_report_df.loc['support'] total = support.sum() avg[-1] = total class_report_df['avg / total'] = avg return class_report_df.T 

Con ranking_report obtendrás algo como:

 print(classification_report(y_true=y_true, y_pred=y_pred, digits=6)) 

Salida:

  precision recall f1-score support 0 0.379032 0.470000 0.419643 400 1 0.579365 0.486667 0.528986 600 avg / total 0.499232 0.480000 0.485248 1000 

Luego, con nuestra función personalizada pandas_classification_report :

 df_class_report = pandas_classification_report(y_true=y_true, y_pred=y_pred) print(df_class_report) 

Salida:

  precision recall f1-score support 0 0.379032 0.470000 0.419643 400.0 1 0.579365 0.486667 0.528986 600.0 avg / total 0.499232 0.480000 0.485248 1000.0 

Luego, simplemente guárdelo en formato csv (consulte aquí para ver otros separadores con formato sep = ‘;’):

 df_class_report.to_csv('my_csv_file.csv', sep=',') 

Abro my_csv_file.csv con LibreOffice Calc (aunque podría usar cualquier editor tabular / de hoja de cálculo como excel): Resultado abierto con LibreOffice

Otra opción es calcular los datos subyacentes y redactar el informe por su cuenta. Todas las estadísticas que obtendrás

 precision_recall_fscore_support 

También encontré algunas de las respuestas un poco detalladas. Aquí está mi solución de tres líneas, utilizando precision_recall_fscore_support como otros han sugerido.

 import pandas as pd from sklearn.metrics import precision_recall_fscore_support report = pd.DataFrame(list(precision_recall_fscore_support(y_true, y_pred)), index=['Precision', 'Recall', 'F1-score', 'Support']).T # Now add the 'Avg/Total' row report.loc['Avg/Total', :] = precision_recall_fscore_support(y_true, y_test, average='weighted') report.loc['Avg/Total', 'Support'] = report['Support'].sum() 

Junto con el ejemplo de entrada-salida, aquí está la otra función metrics_report_to_df () . La implementación de precision_recall_fscore_support desde las métricas de Sklearn debería hacer:

 # Generates classification metrics using precision_recall_fscore_support: from sklearn import metrics import pandas as pd import numpy as np; from numpy import random # Simulating true and predicted labels as test dataset: np.random.seed(10) y_true = np.array([0]*300 + [1]*700) y_pred = np.random.randint(2, size=1000) # Here's the custom function returning classification report dataframe: def metrics_report_to_df(ytrue, ypred): precision, recall, fscore, support = metrics.precision_recall_fscore_support(ytrue, ypred) classification_report = pd.concat(map(pd.DataFrame, [precision, recall, fscore, support]), axis=1) classification_report.columns = ["precision", "recall", "f1-score", "support"] # Add row w "avg/total" classification_report.loc['avg/Total', :] = metrics.precision_recall_fscore_support(ytrue, ypred, average='weighted') classification_report.loc['avg/Total', 'support'] = classification_report['support'].sum() return(classification_report) # Provide input as true_label and predicted label (from classifier) classification_report = metrics_report_to_df(y_true, y_pred) # Here's the output (metrics report transformed to dataframe ) In [1047]: classification_report Out[1047]: precision recall f1-score support 0 0.300578 0.520000 0.380952 300.0 1 0.700624 0.481429 0.570703 700.0 avg/Total 0.580610 0.493000 0.513778 1000.0 
 def to_table(report): report = report.splitlines() res = [] res.append(['']+report[0].split()) for row in report[2:-2]: res.append(row.split()) lr = report[-1].split() res.append([' '.join(lr[:3])]+lr[3:]) return np.array(res) 

devuelve una matriz numpy que se puede convertir en el dataframe pandas o simplemente guardarse como un archivo csv.

Este es mi código para la clasificación de 2 clases (pos, neg).

 report = metrics.precision_recall_fscore_support(true_labels,predicted_labels,labels=classes) rowDicionary["precision_pos"] = report[0][0] rowDicionary["recall_pos"] = report[1][0] rowDicionary["f1-score_pos"] = report[2][0] rowDicionary["support_pos"] = report[3][0] rowDicionary["precision_neg"] = report[0][1] rowDicionary["recall_neg"] = report[1][1] rowDicionary["f1-score_neg"] = report[2][1] rowDicionary["support_neg"] = report[3][1] writer = csv.DictWriter(file, fieldnames=fieldnames) writer.writerow(rowDicionary) 

He modificado la respuesta de @ kindjacket. Prueba esto:

 import collections def classification_report_df(report): report_data = [] lines = report.split('\n') del lines[-5] del lines[-1] del lines[1] for line in lines[1:]: row = collections.OrderedDict() row_data = line.split() row_data = list(filter(None, row_data)) row['class'] = row_data[0] + " " + row_data[1] row['precision'] = float(row_data[2]) row['recall'] = float(row_data[3]) row['f1_score'] = float(row_data[4]) row['support'] = int(row_data[5]) report_data.append(row) df = pd.DataFrame.from_dict(report_data) df.set_index('class', inplace=True) return df 

Puedes exportar ese df a csv usando pandas

No sé si todavía necesita una solución o no, pero es lo mejor que he hecho para mantenerla en un formato perfecto y guardarla:

 def classifcation_report_processing(model_to_report): tmp = list() for row in model_to_report.split("\n"): parsed_row = [x for x in row.split(" ") if len(x) > 0] if len(parsed_row) > 0: tmp.append(parsed_row) # Store in dictionary measures = tmp[0] D_class_data = defaultdict(dict) for row in tmp[1:]: class_label = row[0] for j, m in enumerate(measures): D_class_data[class_label][m.strip()] = float(row[j + 1].strip()) save_report = pd.DataFrame.from_dict(D_class_data).T path_to_save = os.getcwd() +'/Classification_report.xlsx' save_report.to_excel(path_to_save, index=True) return save_report.head(5) saving_CL_report_naive_bayes = classifcation_report_processing(classification_report(y_val, prediction)) 

Tuve el mismo problema que hice, pegue la salida de cadena de metrics.classification_report en hojas de google o sobresalga y divida el texto en columnas por 5 espacios en blanco personalizados.

Solo import pandas as pd y asegúrese de configurar el parámetro output_dict que, de forma predeterminada, es False a True cuando se calcula el output_dict classification_report . Esto dará como resultado un classification_report dictionary pandas DataFrame que luego puede pasar a un método de pandas DataFrame . Es posible que desee transpose el DataFrame resultante para que se ajuste al formato de salida que desee. El DataFrame resultante se puede escribir en un archivo csv como desee.

 clsf_report = pd.DataFrame(classification_report(y_true = your_y_true, y_pred = your_y_preds5, output_dict=True)).transpose() clsf_report.to_csv('Your Classification Report Name.csv', index= True) 

Espero que esto ayude.

Obviamente, es una mejor idea simplemente generar el informe de clasificación como dict :

 sklearn.metrics.classification_report(y_true, y_pred, output_dict=True) 

Pero aquí hay una función que hice para convertir todos los resultados de las clases (solo las clases) en un dataframe de pandas.

 def report_to_csv(report): report = [x.split(' ') for x in report.split('\n')] header = ['Class Name']+[x for x in report[0] if x!=''] values = [] for row in report[1:-5]: row = [value for value in row if value!=''] if row!=[]: values.append(row) df = pd.DataFrame(data = values, columns = header) return None 

Espero que esto funcione bien para ti.

La forma en que siempre he resuelto los problemas de salida es como lo que he mencionado en mi comentario anterior, he convertido mi salida en un DataFrame. No solo es increíblemente fácil de enviar a archivos ( ver aquí ), sino que Pandas es realmente fácil de manipular la estructura de datos. La otra forma en que he resuelto esto es escribiendo la salida línea por línea usando CSV y específicamente usando writerow .

Si logras obtener la salida en un dataframe sería

 dataframe_name_here.to_csv() 

o si utiliza CSV sería algo como el ejemplo que proporcionan en el enlace CSV.