Created
February 28, 2019 18:48
-
-
Save stevecrozz/62899aa338c5aef69597fe374d8659a7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Author: Peter Prettenhofer <peter.prettenhofer@gmail.com> | |
| # Olivier Grisel <olivier.grisel@ensta.org> | |
| # Mathieu Blondel <mathieu@mblondel.org> | |
| # Lars Buitinck | |
| # License: BSD 3 clause | |
| from __future__ import print_function | |
| import re | |
| import logging | |
| import numpy as np | |
| from optparse import OptionParser | |
| import sys | |
| from time import time | |
| import matplotlib.pyplot as plt | |
| from sklearn.datasets import fetch_20newsgroups | |
| from sklearn.feature_extraction.text import TfidfVectorizer | |
| from sklearn.feature_extraction.text import CountVectorizer | |
| from sklearn.feature_extraction.text import HashingVectorizer | |
| from sklearn.feature_selection import SelectFromModel | |
| from sklearn.feature_selection import SelectKBest, chi2 | |
| from sklearn.linear_model import RidgeClassifier | |
| from sklearn.pipeline import Pipeline | |
| from sklearn.svm import LinearSVC | |
| from sklearn.linear_model import SGDClassifier | |
| from sklearn.linear_model import Perceptron | |
| from sklearn.linear_model import PassiveAggressiveClassifier | |
| from sklearn.naive_bayes import BernoulliNB, ComplementNB, MultinomialNB | |
| from sklearn.neighbors import KNeighborsClassifier | |
| from sklearn.neighbors import NearestCentroid | |
| from sklearn.ensemble import RandomForestClassifier | |
| from sklearn.utils.extmath import density | |
| from sklearn import metrics | |
| # Display progress logs on stdout | |
| logging.basicConfig(level=logging.INFO, | |
| format='%(asctime)s %(levelname)s %(message)s') | |
| # parse commandline arguments | |
| op = OptionParser() | |
| op.add_option("--report", | |
| action="store_true", dest="print_report", | |
| help="Print a detailed classification report.") | |
| op.add_option("--chi2_select", | |
| action="store", type="int", dest="select_chi2", | |
| help="Select some number of features using a chi-squared test") | |
| op.add_option("--confusion_matrix", | |
| action="store_true", dest="print_cm", | |
| help="Print the confusion matrix.") | |
| op.add_option("--top10", | |
| action="store_true", dest="print_top10", | |
| help="Print ten most discriminative terms per class" | |
| " for every classifier.") | |
| op.add_option("--all_categories", | |
| action="store_true", dest="all_categories", | |
| help="Whether to use all categories or not.") | |
| op.add_option("--use_hashing", | |
| action="store_true", | |
| help="Use a hashing vectorizer.") | |
| op.add_option("--n_features", | |
| action="store", type=int, default=2 ** 16, | |
| help="n_features when using the hashing vectorizer.") | |
| op.add_option("--filtered", | |
| action="store_true", | |
| help="Remove newsgroup information that is easily overfit: " | |
| "headers, signatures, and quoting.") | |
| def is_interactive(): | |
| return not hasattr(sys.modules['__main__'], '__file__') | |
| # work-around for Jupyter notebook and IPython console | |
| argv = [] if is_interactive() else sys.argv[1:] | |
| (opts, args) = op.parse_args(argv) | |
| if len(args) > 0: | |
| op.error("this script takes no arguments.") | |
| sys.exit(1) | |
| print(__doc__) | |
| op.print_help() | |
| print() | |
| import psycopg2 | |
| from sklearn.model_selection import train_test_split | |
| conn = psycopg2.connect("dbname=postgres user=postgres host=127.0.0.1") | |
| cur = conn.cursor() | |
| # cur.execute("SELECT description, hcpcs_code FROM charge_master_lines WHERE description IS NOT NULL and hcpcs_code LIKE '1____';") | |
| cur.execute("SELECT description, hcpcs_code FROM charge_master_lines WHERE hcpcs_code IN (SELECT hcpcs_code FROM charge_master_lines WHERE description IS NOT NULL and hcpcs_code LIKE '1____' GROUP BY hcpcs_code HAVING COUNT(description) >= 5);") | |
| X = list() | |
| y = list() | |
| canonical_phrases = { | |
| 'FINE NEEDLE ASPIRATION': 'FNA', | |
| 'FINE NEEDLE': 'FNE NDL', | |
| 'WITHOUT GUIDANCE': 'WO GUIDE', | |
| 'WITH GUIDANCE': 'W GUIDE', | |
| 'IMAGING GUIDANCE': 'IMAGINE GUID', | |
| 'NEEDLE ASPIRATION': 'NDL ASPIR', | |
| 'NEEDLE ASPIRATION': 'NEEDLE ASP', | |
| 'NEEDLE': 'NDL', | |
| 'NEEDLE': 'NDLE', | |
| 'WITHOUT': 'W/O', | |
| 'WITHOUT': 'WO', | |
| 'WITHOUT': 'W\\O', | |
| 'WITHOUT': 'W/OUT', | |
| 'WITH': 'W/', | |
| 'WITH': 'W\\', | |
| 'WITH': 'W', | |
| 'IMAGE': 'IMAG\\', | |
| 'IMAGE': 'IMAG\\', | |
| 'IMAGE': 'IMG\\', | |
| 'CATHETER': 'CATHET\\', | |
| 'CATHETER': 'CATH\\', | |
| 'SOFT TISSUE': 'SFT TISS\\', | |
| 'COLLECTION': 'COLL\\', | |
| 'SURGERY': 'SUGE', | |
| 'CYSTS': 'SYSTS', | |
| 'INCISION DRAINAGE': 'I+D', | |
| 'INCISION DRAINAGE': 'I/D', | |
| 'INCISION DRAINAGE': 'I & D', | |
| 'INCISION DRAINAGE': 'INCISION & DRAINAGE', | |
| 'INCISION DRAINAGE': 'I D', | |
| 'FLUID COLLECTION': 'FLD COLL', | |
| 'ABSCESS': 'ABSC', | |
| 'ABSCESS': 'ABCESS', | |
| 'SUBCUTANEOUS': 'SUBCUT', | |
| 'SUBCUTANEOUS': 'SQ', | |
| 'SUBCUTANEOUS': 'SUBQ', | |
| 'SIMPLE OR SINGLE': 'SMPL OR SGL', | |
| 'SIMPLE OR SINGLE': 'SIMPL/SNGL', | |
| 'SIMPLE OR SINGLE': 'SIMPLE/SINGLE', | |
| 'SIMPLE OR SINGLE': 'SIMPLE OR SINGLE', | |
| 'SIMPLE OR SINGLE': 'SIMPLE OR SINGL', | |
| 'COMPLEX OR MULTIPLE': 'COMPL OR MULTI', | |
| 'COMPLEX OR MULTIPLE': 'COMPLIC/MX', | |
| 'COMPLEX OR MULTIPLE': 'COMPLX OR MULTI', | |
| 'COMPLEX OR MULTIPLE': 'COMPLEX/MULTI', | |
| 'COMPLEX OR MULTIPLE': 'COMP/MULT', | |
| 'COMPLEX OR MULTIPLE': 'CMPX/MULT', | |
| 'COMPLEX': 'COMPLICATED', | |
| 'COMPLEX': 'CPLX', | |
| 'MULTIPLE': 'MULT', | |
| 'DEBRIDE': 'DEBRID', | |
| 'DEBRIDE': 'DEBRIDEMENT', | |
| 'BIOPSY': 'BX', | |
| 'BODY': 'BDY', | |
| 'FOREIGN BODY': 'FB', | |
| 'SIMPLE': 'SMPL', | |
| } | |
| canonical_phrase_compiled = [] | |
| for k, v in canonical_phrases.items(): | |
| canonical_phrase_compiled.append( | |
| { 'r': k, 'p': re.compile(r"\b" + re.escape(v) + "\b", flags=re.IGNORECASE) }) | |
| def canonicalize_phrase(description): | |
| for item in canonical_phrase_compiled: | |
| description = re.sub(item['p'], item['r'], description) | |
| return description | |
| for record in cur: | |
| X.append(canonicalize_phrase(record[0])) | |
| if len(record[1]) == 5 and record[1].startswith('1'): | |
| y.append(record[1]) | |
| else: | |
| y.append(None) | |
| def size_mb(docs): | |
| return sum(len(s.encode('utf-8')) for s in docs) / 1e6 | |
| Xtrain, Xtest, ytrain, ytest = train_test_split( | |
| X, y, test_size=0.33, random_state=42) | |
| data_train_size_mb = size_mb(Xtrain) | |
| data_test_size_mb = size_mb(Xtest) | |
| target_names = set(y) | |
| print("%d documents - %0.3fMB (training set)" % ( | |
| len(Xtrain), data_train_size_mb)) | |
| print("%d documents - %0.3fMB (test set)" % ( | |
| len(Xtest), data_test_size_mb)) | |
| print("%d categories" % len(target_names)) | |
| print() | |
| print("Extracting features from the training data using a sparse vectorizer") | |
| t0 = time() | |
| if opts.use_hashing: | |
| vectorizer = HashingVectorizer(stop_words='english', alternate_sign=False, | |
| n_features=opts.n_features) | |
| X_train = vectorizer.transform(Xtrain) | |
| else: | |
| vectorizer = TfidfVectorizer( | |
| sublinear_tf=True, | |
| # max_df=0.5, | |
| token_pattern=r"(?u)\b[-&\w][-&\w]+\b", | |
| # ngram_range=[1, 3], | |
| stop_words=None) | |
| X_train = vectorizer.fit_transform(Xtrain) | |
| duration = time() - t0 | |
| print("done in %fs at %0.3fMB/s" % (duration, data_train_size_mb / duration)) | |
| print("n_samples: %d, n_features: %d" % X_train.shape) | |
| print() | |
| print("Extracting features from the test data using the same vectorizer") | |
| t0 = time() | |
| X_test = vectorizer.transform(Xtest) | |
| duration = time() - t0 | |
| print("done in %fs at %0.3fMB/s" % (duration, data_test_size_mb / duration)) | |
| print("n_samples: %d, n_features: %d" % X_test.shape) | |
| print() | |
| # mapping from integer feature name to original token string | |
| if opts.use_hashing: | |
| feature_names = None | |
| else: | |
| feature_names = vectorizer.get_feature_names() | |
| print(sorted(feature_names)) | |
| if opts.select_chi2: | |
| print("Extracting %d best features by a chi-squared test" % | |
| opts.select_chi2) | |
| t0 = time() | |
| ch2 = SelectKBest(chi2, k=opts.select_chi2) | |
| X_train = ch2.fit_transform(X_train, ytrain) | |
| X_test = ch2.transform(X_test) | |
| if feature_names: | |
| # keep selected feature names | |
| feature_names = [feature_names[i] for i | |
| in ch2.get_support(indices=True)] | |
| print("done in %fs" % (time() - t0)) | |
| print() | |
| if feature_names: | |
| feature_names = np.asarray(feature_names) | |
| def trim(s): | |
| """Trim string to fit on terminal (assuming 80-column display)""" | |
| return s if len(s) <= 80 else s[:77] + "..." | |
| # ############################################################################# | |
| # Benchmark classifiers | |
| def benchmark(clf): | |
| print('_' * 80) | |
| print("Training: ") | |
| print(clf) | |
| t0 = time() | |
| clf.fit(X_train, ytrain) | |
| train_time = time() - t0 | |
| print("train time: %0.3fs" % train_time) | |
| t0 = time() | |
| pred = clf.predict(X_test) | |
| test_time = time() - t0 | |
| print("test time: %0.3fs" % test_time) | |
| score = metrics.accuracy_score(ytest, pred) | |
| print("accuracy: %0.3f" % score) | |
| if hasattr(clf, 'coef_'): | |
| print("dimensionality: %d" % clf.coef_.shape[1]) | |
| print("density: %f" % density(clf.coef_)) | |
| if opts.print_top10 and feature_names is not None: | |
| print("top 10 keywords per class:") | |
| for i, label in enumerate(target_names): | |
| top10 = np.argsort(clf.coef_[i])[-10:] | |
| print(trim("%s: %s" % (label, " ".join(feature_names[top10])))) | |
| print() | |
| if opts.print_report: | |
| print("classification report:") | |
| print(metrics.classification_report(ytest, pred, | |
| target_names=target_names)) | |
| if opts.print_cm: | |
| print("confusion matrix:") | |
| print(metrics.confusion_matrix(ytest, pred)) | |
| print() | |
| clf_descr = str(clf).split('(')[0] | |
| return clf_descr, score, train_time, test_time | |
| results = [] | |
| for clf, name in ( | |
| (RidgeClassifier(tol=1e-2, solver="sag"), "Ridge Classifier"), | |
| (Perceptron(max_iter=50, tol=1e-3), "Perceptron"), | |
| (PassiveAggressiveClassifier(max_iter=50, tol=1e-3), | |
| "Passive-Aggressive"), | |
| (KNeighborsClassifier(n_neighbors=10), "kNN"), | |
| (RandomForestClassifier(n_estimators=100), "Random forest")): | |
| print('=' * 80) | |
| print(name) | |
| results.append(benchmark(clf)) | |
| for penalty in ["l2", "l1"]: | |
| print('=' * 80) | |
| print("%s penalty" % penalty.upper()) | |
| # Train Liblinear model | |
| results.append(benchmark(LinearSVC(penalty=penalty, dual=False, | |
| tol=1e-3))) | |
| # Train SGD model | |
| results.append(benchmark(SGDClassifier(alpha=.0001, max_iter=50, | |
| penalty=penalty))) | |
| # Train SGD with Elastic Net penalty | |
| print('=' * 80) | |
| print("Elastic-Net penalty") | |
| results.append(benchmark(SGDClassifier(alpha=.0001, max_iter=50, | |
| penalty="elasticnet"))) | |
| # Train NearestCentroid without threshold | |
| print('=' * 80) | |
| print("NearestCentroid (aka Rocchio classifier)") | |
| results.append(benchmark(NearestCentroid())) | |
| # Train sparse Naive Bayes classifiers | |
| print('=' * 80) | |
| print("Naive Bayes") | |
| results.append(benchmark(MultinomialNB(alpha=.01))) | |
| results.append(benchmark(BernoulliNB(alpha=.01))) | |
| results.append(benchmark(ComplementNB(alpha=.1))) | |
| print('=' * 80) | |
| print("LinearSVC with L1-based feature selection") | |
| # The smaller C, the stronger the regularization. | |
| # The more regularization, the more sparsity. | |
| results.append(benchmark(Pipeline([ | |
| ('feature_selection', SelectFromModel(LinearSVC(penalty="l1", dual=False, | |
| tol=1e-3))), | |
| ('classification', LinearSVC(penalty="l2"))]))) | |
| # make some plots | |
| indices = np.arange(len(results)) | |
| results = [[x[i] for x in results] for i in range(4)] | |
| clf_names, score, training_time, test_time = results | |
| training_time = np.array(training_time) / np.max(training_time) | |
| test_time = np.array(test_time) / np.max(test_time) | |
| plt.figure(figsize=(12, 8)) | |
| plt.title("Score") | |
| plt.barh(indices, score, .2, label="score", color='navy') | |
| plt.barh(indices + .3, training_time, .2, label="training time", | |
| color='c') | |
| plt.barh(indices + .6, test_time, .2, label="test time", color='darkorange') | |
| plt.yticks(()) | |
| plt.legend(loc='best') | |
| plt.subplots_adjust(left=.25) | |
| plt.subplots_adjust(top=.95) | |
| plt.subplots_adjust(bottom=.05) | |
| for i, c in zip(indices, clf_names): | |
| plt.text(-.3, i, c) | |
| plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment