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:
Hilfe geschätzt Vielen Dank!
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)
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
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
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
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):
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.
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
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()
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.
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
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)
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.
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
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))
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.