web-dev-qa-db-de.com

scikit lernt die Ausgabe von metrics.classification_report im CSV/tabulatorgetrennten Format

Ich mache eine Klassifizierung von Multiklassen in Scikit-Learn. Der Datensatz wird mit dem multinomialen Naive Bayes-Klassifikator mit Hunderten von Labels trainiert. Hier ist ein Auszug aus dem Scikit Learn-Skript zur Anpassung des MNB-Modells

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))

Eine vereinfachte Ausgabe von metrics.classification_report auf dem Befehlszeilenbildschirm sieht folgendermaßen aus:

             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

Ich habe mich gefragt, ob es eine Möglichkeit gibt, die Berichtsausgabe in eine Standard-CSV-Datei mit normalen Spaltenüberschriften zu bekommen

Wenn ich die Befehlszeilenausgabe in eine CSV-Datei sende oder versuche, die Bildschirmausgabe in eine Tabelle (Openoffice Calc oder Excel) zu kopieren/einzufügen, werden die Ergebnisse in einer Spalte zusammengefasst. Sieht so aus:

 enter image description here

Hilfe geschätzt Vielen Dank!

13
Seun AJAO

Wenn Sie die einzelnen Ergebnisse wünschen, sollte dies die Arbeit gut machen.

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)
9
kindjacket

Wir können die tatsächlichen Werte von der precision_recall_fscore_support-Funktion abrufen und sie dann in Datenrahmen einfügen.

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
7
rollthedice32

Während die vorherigen Antworten wahrscheinlich alle funktionieren, fand ich sie etwas wortreich. Im Folgenden werden die einzelnen Klassenergebnisse sowie die Zusammenfassungszeile in einem einzelnen Datenrahmen gespeichert. Nicht sehr anfällig für Änderungen im Bericht, aber für mich der Trick.

#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)

Was ergibt die gewünschte Ausgabe:

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
6
Kam Sen

Ab scikit-learn v0.20 besteht die einfachste Möglichkeit zum Konvertieren eines Klassifizierungsberichts in ein pandas Dataframe darin, dass der Bericht einfach als dict zurückgegeben wird:

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

erstellen Sie dann ein Dataframe und transponieren Sie es:

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

Von hier aus können Sie die Standard pandas-Methoden verwenden, um die gewünschten Ausgabeformate (CSV, HTML, LaTeX, ...) zu generieren.

Siehe auch die Dokumentation unter https://scikit-learn.org/0.20/modules/generated/sklearn.metrics.classification_report.html

4
janus235

Wie in einem der Beiträge hier erwähnt, ist precision_recall_fscore_support analog zu classification_report

Dann genügt es, die Python-Bibliothek pandas zu verwenden, um die Daten auf einfache Weise in einem Spaltenformat zu formatieren, ähnlich wie classification_report. Hier ist ein Beispiel:

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

Mit classification_report erhalten Sie so etwas wie:

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

Ausgabe:

             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

Dann mit unserer benutzerdefinierten Funktion pandas_classification_report:

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

Ausgabe:

             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

Dann speichern Sie es einfach im csv-Format (siehe hier für andere Trennzeichen, die wie sep = ';' formatieren):

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

Ich my_csv_file.csv mit LibreOffice Calc öffnen (obwohl Sie einen Tabellen-/Tabelleneditor wie Excel verwenden könnten):  Result open with LibreOffice

3
Raul

Einfach import pandas as pd Und stellen Sie sicher, dass Sie den Parameter output_dict, Der standardmäßig False ist, auf True setzen, wenn Sie den classification_report Berechnen. Dies führt zu einem classification_report dictionary, Den Sie dann an eine pandas DataFrame - Methode übergeben können. Möglicherweise möchten Sie transpose das resultierende DataFrame an das gewünschte Ausgabeformat anpassen. Das resultierende DataFrame kann dann nach Belieben in eine csv Datei geschrieben werden.

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)

Ich hoffe das hilft.

2
Samuel Nde

Eine andere Möglichkeit besteht darin, die zugrunde liegenden Daten zu berechnen und den Bericht selbst zu erstellen. Alle Statistiken, die Sie erhalten werden

precision_recall_fscore_support
2
Karel Macek

Ich fand auch einige Antworten ein wenig ausführlich. Hier ist meine dreizeilige Lösung, die precision_recall_fscore_support verwendet, wie andere vorgeschlagen haben.

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()
1
elphz

Es ist offensichtlich eine bessere Idee, den Klassifizierungsbericht einfach als dict auszugeben:

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

Aber hier ist eine Funktion, die ich gemacht habe, um alle Klassen (nur Klassen) Ergebnisse in einen pandas dataframe) zu konvertieren.

def report_to_df(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 df

Hoffe, das funktioniert gut für Sie.

1
Yash Nag

Neben der Beispieleingabe-Ausgabehier ist die andere Funktionmetrics_report_to_df (). Die Implementierung von precision_recall_fscore_support aus Sklearn-Metriken sollte folgende Schritte ausführen:

# 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
1
Surya

Dies ist mein Code für die Klassifizierung in 2 Klassen (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)
0
Piotr Badura
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)

gibt ein numpy-Array zurück, das in Pandas-Datenrahmen umgewandelt oder als CSV-Datei gespeichert werden kann.

0
Sipan17

Ich habe die Antwort von @ kindjacket geändert. Versuchen Sie Folgendes:

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

Sie können diese df nur mit Pandas in csv exportieren

0
Kevin

Ich weiß nicht, ob Sie immer noch eine Lösung benötigen oder nicht, aber dies ist das Beste, was ich getan habe, um es im perfekten Format zu halten und es trotzdem zu speichern:

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))
0
DeshDeep Singh

Ich hatte das gleiche Problem, was ich tat, war, die Zeichenfolgenausgabe von metrics.classification_report in Google Sheets oder Excel einzufügen und den Text durch benutzerdefinierte 5 Whitespaces in Spalten aufzuteilen.

0
Nitin Kumar