Scikit learn output metrics.classification_report в формате CSV/tab-limit

Я делаю мультиклассовую классификацию текста в Scikit-Learn. Набор данных обучается с использованием полиномиального наивного байесовского классификатора, имеющего сотни меток. Вот выдержка из скрипта Scikit Learn для подгонки модели 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))

И упрощенный вывод metrics.classification_report на экране командной строки выглядит следующим образом:

             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

Мне было интересно, если есть какой-либо способ получить вывод отчета в стандартный файл CSV с обычными заголовками столбцов

Когда я отправляю вывод командной строки в файл csv или пытаюсь скопировать/вставить вывод экрана в электронную таблицу - Openoffice Calc или Excel, он объединяет результаты в одном столбце. Выглядит так:

enter image description here

Помощь приветствуется. Спасибо!

Ответ 1

Если вы хотите получить индивидуальные оценки, это должно хорошо сработать.

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)

Ответ 2

Начиная с scikit-learn v0.20, самый простой способ преобразовать отчет о классификации в pandas Dataframe - это просто вернуть отчет как dict:

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

а затем создайте Dataframe и транспонируйте его:

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

С этого момента вы можете использовать стандартные методы pandas для генерации желаемых форматов вывода (CSV, HTML, LaTeX,...).

См. Также документацию по адресу https://scikit-learn.org/0.20/modules/generated/sklearn.metrics.classification_report.html.

Ответ 3

Мы можем получить фактические значения из функции precision_recall_fscore_support, а затем поместить их в кадры данных. приведенный ниже код даст тот же результат, но теперь в 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

Ответ 4

Хотя предыдущие ответы, вероятно, все работают, я нашел их немного подробными. В следующем списке хранятся отдельные результаты класса, а также сводная строка в одном кадре данных. Не очень чувствителен к изменениям в отчете, но помогло.

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

Что дает желаемый результат:

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

Ответ 5

Как упомянуто в одном из сообщений здесь, precision_recall_fscore_support аналогичен classification_report.

Тогда достаточно использовать библиотеки pandas Python для простого форматирования данных в столбчатом формате, аналогично тому, как это делает classification_report. Вот пример:

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

С classification_report вы получите что-то вроде:

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

Выход:

             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

Затем с нашей пользовательской pandas_classification_report:

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

Выход:

             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

Затем просто сохраните его в формате csv (см. Здесь для другого форматирования разделителя, как sep = ';'):

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

Я открываю my_csv_file.csv с помощью LibreOffice Calc (хотя вы можете использовать любой редактор таблиц/электронных таблиц, например, Excel): Result open with LibreOffice

Ответ 6

Другой вариант - рассчитать базовые данные и составить отчет самостоятельно. Все статистические данные, которые вы получите по

precision_recall_fscore_support

Ответ 7

Я также нашел некоторые ответы несколько многословными. Вот мое трехстрочное решение, использующее precision_recall_fscore_support как предлагали другие.

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

Ответ 8

Наряду с примером ввода-вывода, здесь используется другая функция metrics_report_to_df(). Реализация precision_recall_fscore_support из показателей Sklearn должна сделать:

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

Ответ 9

Просто import pandas as pd и убедитесь, что вы установили output_dict параметр, который по умолчанию значение False, чтобы True при вычислении classification_report. Это приведет к появлению classification_report dictionary который затем можно будет передать в метод pandas DataFrame. Возможно, вы захотите transpose результирующий DataFrame чтобы он соответствовал DataFrame выходному формату. Полученный в результате DataFrame может быть записан в csv файл по вашему желанию.

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)

Надеюсь, это поможет.

Ответ 10

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)

возвращает массив numpy, который можно преобразовать в фреймворк pandas или просто сохранить как файл csv.

Ответ 11

Это мой код для классификации 2 классов (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)

Ответ 12

Я изменил ответ @kindjacket. Попробуй это:

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

Вы можете просто экспортировать этот df в csv используя pandas

Ответ 13

Я не знаю, нужно ли вам решение или нет, но это лучшее, что я сделал, чтобы сохранить его в идеальном формате и сохранить его:

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

Ответ 14

У меня была та же проблема, что и у меня: вставьте строковый вывод metrics.classification_report в листы Google или Excel и разделите текст на столбцы с помощью пользовательских 5 пробелов.

Ответ 15

Очевидно, что лучше выводить отчет о классификации как dict:

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

Но здесь функция, которую я сделал, чтобы преобразовать все классы (только классы) результатов в pandas dataframe.

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

Надеюсь, это хорошо для вас.

Ответ 16

То, как я всегда решал проблемы вывода, похоже на то, что я упомянул в предыдущем комментарии, я преобразовал свой вывод в DataFrame. Не просто невероятно легко отправить файлы (см. Здесь), но Pandas действительно легко манипулировать структурой данных. Другим способом, который я решил, является запись вывода по строкам с помощью CSV и, в частности, с помощью writerow.

Если вам удастся получить вывод в кадре данных, это будет

dataframe_name_here.to_csv()

или при использовании CSV это будет похоже на пример, который они предоставляют в CSV-ссылке.