Repository: DistrictDataLabs/yellowbrick

Issue:
Submit a Yellowbrick Paper to the Journal of Open Source Software
The [Journal of Open Source Software (JOSS)](http://arfon.org/announcing-the-journal-of-open-source-software/) is a developer-friendly journal for research software packages and is affiliated with the Open Source Initiative (OSI) and sponsored by NumFOCUS. For a long time, we've wanted to publish a paper related to Yellowbrick, and this might be a good start. 

The paper should be written in Markdown and be between 250-1000 words. Principally, it should contain a summary describing high-level functionality for a non-specialist audience with a clear statement of purpose.  It should also include a bibliography and a metadata file that can be read by JOSS reviewers. 

High level tasks:

- [x] determine who the authors are 
- [x] create a bibliography in paper/paper.bib 
- [x] generate the metadata in paper/paper.json with this [script](https://gist.github.com/arfon/478b2ed49e11f984d6fb)
- [x] write outline of the paper 
- [x] write first draft of the paper 
- [x] review and edit draft 
- [x] submit to JOSS

Requirements:

- [x] The software should be open source as per the OSI definition
- [ ] The software should have an obvious research application
- [x] You should be a major contributor to the software you are submitting
- [x] The software should be a significant contribution to the available open source software that either enables some new research challenges to be addressed or makes addressing research challenges significantly better (e.g., faster, easier, simpler)
- [x] The software should be feature complete (no half-baked solutions) Minor, ‘utility’ packages, including ‘thin’ API clients are not acceptable
- [x] Be stored in a repository that can be cloned without registration
- [x] Be stored in a repository that is browsable online without registration
- [x] Have an issue tracker that is readable without registration
- [x] Permit individuals to create issues/file tickets against your repository


Discussion:
@rebeccabilbro do you want to start putting together the bibliography and I'll start with the outline and the draft, then we can switch? 

Resolving patch:
diff --git a/paper/figures/figures.py b/paper/figures/figures.py
new file mode 100644
--- /dev/null
+++ b/paper/figures/figures.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+# Script to create visualizations for the JOSS paper
+
+import os
+import argparse
+import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+
+from yellowbrick.features import Rank2D, RadViz
+from yellowbrick.model_selection import LearningCurve
+from yellowbrick.cluster import KElbowVisualizer, SilhouetteVisualizer
+from yellowbrick.classifier import ClassificationReport, DiscriminationThreshold
+from yellowbrick.regressor import ResidualsPlot, PredictionError, AlphaSelection
+
+from collections import namedtuple
+from sklearn.datasets import make_blobs
+from sklearn.naive_bayes import MultinomialNB
+from sklearn.ensemble import RandomForestRegressor
+from sklearn.cluster import MiniBatchKMeans, Birch
+from sklearn.model_selection import train_test_split as tts
+from sklearn.linear_model import LassoCV, RidgeCV, LogisticRegression
+
+
+# Store figures alongside the script that generates them
+FIGURES = os.path.dirname(__file__)
+
+# Path to datasets downloaded from S3
+DATA = os.path.join(
+    os.path.dirname(__file__), "..", "..", "yellowbrick", "datasets", "fixtures"
+)
+
+# Quick reference dataset objects
+Dataset = namedtuple('Dataset', 'X,y')
+Split = namedtuple('Split', 'train,test')
+
+
+def _make_dataset(X, y, split=False):
+    if split:
+        X_train, X_test, y_train, y_test = tts(X, y, test_size=0.2)
+        return Dataset(Split(X_train, X_test), Split(y_train, y_test))
+    return Dataset(X, y)
+
+
+def load_occupancy(split=False):
+    """
+    Create a dataset for the specified yb dataset
+    """
+    path = os.path.join(DATA, "occupancy", "occupancy.csv")
+    data = pd.read_csv(path)
+
+    X = data[["temperature", "relative humidity", "light", "C02", "humidity"]]
+    y = data["occupancy"]
+    return _make_dataset(X, y, split)
+
+
+def load_concrete(split=False):
+    path = os.path.join(DATA, "concrete", "concrete.csv")
+    data = pd.read_csv(path)
+
+    X = data[['cement', 'slag', 'ash', 'water', 'splast', 'coarse', 'fine', 'age']]
+    y = data['strength']
+    return _make_dataset(X, y, split)
+
+
+def load_spam(split=False):
+    path = os.path.join(DATA, "spam", "spam.csv")
+    data = pd.read_csv(path)
+
+    target = "is_spam"
+    features = [col for col in data.columns if col != target]
+
+    X = data[features]
+    y = data[target]
+    return _make_dataset(X, y, split)
+
+
+def feature_analysis(fname="feature_analysis.png"):
+    """
+    Create figures for feature analysis
+    """
+
+    # Create side-by-side axes grid
+    _, axes = plt.subplots(ncols=2, figsize=(18,6))
+
+    # Draw RadViz on the left
+    data = load_occupancy(split=False)
+    oz = RadViz(ax=axes[0], classes=["unoccupied", "occupied"])
+    oz.fit(data.X, data.y)
+    oz.finalize()
+
+    # Draw Rank2D on the right
+    data = load_concrete(split=False)
+    oz = Rank2D(ax=axes[1])
+    oz.fit_transform(data.X, data.y)
+    oz.finalize()
+
+    # Save figure
+    path = os.path.join(FIGURES, fname)
+    plt.tight_layout()
+    plt.savefig(path)
+
+
+def regression(fname="regression.png"):
+    """
+    Create figures for regression models
+    """
+    _, axes = plt.subplots(ncols=2, figsize=(18, 6))
+    alphas = np.logspace(-10, 1, 300)
+    data = load_concrete(split=True)
+
+    # Plot prediction error in the middle
+    oz = PredictionError(LassoCV(alphas=alphas), ax=axes[0])
+    oz.fit(data.X.train, data.y.train)
+    oz.score(data.X.test, data.y.test)
+    oz.finalize()
+
+    # Plot residuals on the right
+    oz = ResidualsPlot(RidgeCV(alphas=alphas), ax=axes[1])
+    oz.fit(data.X.train, data.y.train)
+    oz.score(data.X.test, data.y.test)
+    oz.finalize()
+
+    # Save figure
+    path = os.path.join(FIGURES, fname)
+    plt.tight_layout()
+    plt.savefig(path)
+
+
+def classification(fname="classification.png"):
+
+    # Create side-by-side axes grid
+    _, axes = plt.subplots(ncols=2, figsize=(18,6))
+
+    # Add ClassificationReport to the reft
+    data = load_spam(split=True)
+    oz = ClassificationReport(MultinomialNB(), classes=["ham", "spam"], ax=axes[0])
+    oz.fit(data.X.train, data.y.train)
+    oz.score(data.X.test, data.y.test)
+    oz.finalize()
+
+    # Add DiscriminationThreshold to the right
+    data = load_spam(split=False)
+    oz = DiscriminationThreshold(LogisticRegression(), ax=axes[1])
+    oz.fit(data.X, data.y)
+    oz.finalize()
+
+    # Save figure
+    path = os.path.join(FIGURES, fname)
+    plt.tight_layout()
+    plt.savefig(path)
+
+
+def clustering(fname="clustering.png"):
+    # Create side-by-side axes grid
+    _, axes = plt.subplots(ncols=2, figsize=(18,6))
+    X, y = make_blobs(centers=7)
+
+    # Add K-Elbow to the left
+    oz = KElbowVisualizer(MiniBatchKMeans(), k=(3,12), ax=axes[0])
+    oz.fit(X, y)
+    oz.finalize()
+
+    # Add SilhouetteVisualizer to the right
+    oz = SilhouetteVisualizer(Birch(n_clusters=5), ax=axes[1])
+    oz.fit(X, y)
+    oz.finalize()
+
+    # Save figure
+    path = os.path.join(FIGURES, fname)
+    plt.tight_layout()
+    plt.savefig(path)
+
+def hyperparameter_tuning(fname="hyperparameter_tuning.png"):
+    # Create side-by-side axes grid
+    _, axes = plt.subplots(ncols=2, figsize=(18,6))
+
+    # Load the concrete dataset
+    data = load_concrete(split=False)
+
+    # Create a list of alphas to cross-validate against
+    alphas = np.logspace(-10, 1, 400)
+
+    # Add AlphaSelection to the left
+    oz = AlphaSelection(LassoCV(alphas=alphas), ax=axes[0])
+    oz.fit(data.X, data.y)
+    oz.finalize()
+
+    # Add LearningCurve to the right
+    oz = LearningCurve(RandomForestRegressor(), scoring='r2', ax=axes[1])
+    oz.fit(data.X, data.y)
+    oz.finalize()
+
+    # Save figure
+    path = os.path.join(FIGURES, fname)
+    plt.tight_layout()
+    plt.savefig(path)
+
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(
+        description="generate visualizations for JOSS paper"
+    )
+
+    args = parser.parse_args()
+    feature_analysis()
+    regression()
+    classification()
+    clustering()
+    hyperparameter_tuning()


Test patch: