Wednesday, March 15, 2017

How much money has OK spent on common education


Norman Public Schools is a school district near the University of Oklahoma. NPS serves nearly 16 k students in pre-Kindergarten through 12th grade. It has 17 elementary schools, 4 middle schools, 2 high schools(Norman High, Norman North High), 1 alternative school and 1 online program. For example, Norman High, 4-year. 127 faculties, 1.9 k student, a ratio of 1:18.

Finacial report by Brenda Burkett on 2016.2.10

For the whole Oklahoma state, how funding is changed during the past 8 years:
2008 2016
total state aid funding, 2.05 B 1.85 B
total students 641.7 k 692.8 k
aid per student 3.2 k 2.70 k
state aid formula, per weighted student 3.29 k 3.05 k
total spending 5.96 B
spending per pupil 8.85 k
As I noted in my previous post. OK government has a total appropriation of 5.7B. It spends 3.4 B in education, among which 0.96 B is in higher education. The about 1/3 of government money is spent on k-12 education, or common education.
The school fundings have many different sources with different targeted purposes:
  • general fund. basic operational can support service, such as teacher and support salaries, health insurance, instructional supplies, textbooks, transportation.
  • building fund.
  • child nutrition fund.
  • bond fund.
  • sinking fund
  • school activity fund.
GeneralFund
The school districts in richer cities has more percentage of money from local.

top 10 question about school funding

state aid funding formula is a metric used to allocate state money to individual district by the student number. The weight factor includes grade level, special education, economically disadvantaged, bilingual, district characteristics. It is a way to equalize revenue among districts. For example, more local revenue collection, less aid from the state.
schools are affected by the sequestration due to the Federal funding cut.
Although Oklahoma law does mandate max class size (20~25), the inadequate funding to staff leads to the fact that legislation is passed to remove the penalties for non-compliance.

Amended school budget and financing plan

total amended budget of appropriated funds is $ 112 M, which includes 110 M General fund, 4.6 M for building fund, and 4.9 M for Child Nutrition Fund. The name of the fund is pack something together as a black box. Specifically, 31.5 M is from Ad Valorem Tax, a latin name for “property tax”. 48 M is from State aid for general operations. Other sources are less than 4 M each.
As OKpolicy explains, Property tax, also known as ad valorem tax, is an annual tax paid by property owners to local government. Property tax collections in Oklahoma totaled $2.2 billion in 2011 and are the single largest source of local government revenue. The math is simple: property tax = (property valuation * assessment ratio - exemption) * millage levy. For example, a house worth 100 k will pay annual tax = (100 k * 0.12 - 1 k) * 0.11 = 1.21 k. It is interesting that mileage levy has a unit of 0.001.
According to salary.com, Oklahoma Teacher High School Salaries range from 50 k to 56 k. Note the median household income is 46 k for Oklahoma and 52 k for the US.

Tuesday, March 14, 2017

Plotly is eating the data science market

Plotly is my new love after dimple failed to draw a simple pie chart. It turns out that, Plotly is doing the best job than any others that make every data analytics tool (e.g., python, R, Matlab) immediately ready for web-based data visualization. If you have complicated data and want to publish your finding online, look no further, python+ plotly is your best choice.
The founder of Plotly, Alex Johnson, has an interesting career path. He got his Harvard PhD in Physics in 2005, research on “Charge Sensing and Spin Dynamics in GaAs Quantum Dots” with a National Science Foundation graduate research fellowship. After one year of postdoc, he went to Harvard Environment Center for 3 years. He was trying to develop novel thin-film solid oxide fuel by applying semiconductor techniques such as microfabrication. He then spent 1 year at C12 Energy design and built a database and web interface for screening and forecasting enhanced oil recovery projects.
In 2012, he founded Plotly, which is a JavaScript graphing library:
  • comparable in scope and features to MATLAB or Python’s matplotlib.
  • It has D3 and WebGL for backend. no need for jQuery.
  • use JSON schema,It focuses on the chart’s physical attributes and attempts to leave the chart data separate.
  • In contrast, The vega and vega-lite schemas are more opinionated in prescribing how the chart data is grouped, sliced, or statistically processed before graphical display. This allows for complicated chart display with a concise JSON description, but leaves less control to the user. Neither approach is more “correct”—they’re just different.
Since 2015. 11, Plotly was open-source at https://github.com/plotly/plotly.js The business model for plotly is by charging the API of python. matlab, R, similar to the charge of Google map API. The community version is free for upto 50 API calls per day. More advanced plotting and more API calls are charged. Personal plan is $33 per month and student plan is $5 per month.
Due to time constraint, I only have a quick practice with the “getting started” for each language. Much more APIs are found here: https://plot.ly/api/

JavaScript

<script src=”https://cdn.plot.ly/plotly-latest.min.js”>
Basic Box plot
var y0,y1;
for (var i = 0; i < 50; i ++) {
    y0[i] = Math.random();
    y1[i] = Math.random() + 1;
}
var trace1 = {
  y: y0,
  type: 'box'
};
var trace2 = {
  y: y1,
  type: 'box'
};
Plotly.newPlot('myDiv', [trace1, trace2]);

python API

install the python API: pip install plotly
import plotly
plotly.tools.set_credentials_file(username='jychstar', api_key='1GPp9Dwmnsf897Z3kX8Q')  # now I use a free api key, info stored at .plotly/.credentials file in  home directory
plotly.tools.set_config_file(world_readable=False,
                             sharing='private') # public, private, or secret
import plotly.plotly as py
import plotly.graph_objs as go
trace0 = go.Scatter(
    x=[1, 2, 3, 4],
    y=[10, 15, 13, 17]
)
trace1 = go.Scatter(
    x=[1, 2, 3, 4],
    y=[16, 5, 11, 9]
)
figure = go.Figure(
    data = [trace0, trace1],
    layout = go.Layout (title = "hello world" )
)

py.plot(figure, filename = 'basic-line')  # plot online
py.iplot(figure, filename = 'basic-line') # plot inline
plotly.offline.plot(figure) # plot offline in browser
plotly.offline.iplot(figure) # plot offline in notebook
If you are interested what methods can be called by go, using dir(plotly.graph_objs)will give 58 items such as Area, Bar, Box, Candlestic,, HeatMmap, Histogram, Line, Pie, Scatter, Trace.
pie
labels=['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen']
values=[4500,2500,1053,500]
colors = ['#FAEE1C', '#F3558E', '#9C1DE7', '#581B98']
trace = go.Pie(labels=labels,values=values,marker={'colors': colors})
url_2 = py.plot([trace], filename='pie-for-dashboard', auto_open=True)
py.iplot([trace], filename='pie-for-dashboard')

pandas

use a fresh-bake model cufflinks , pandas can directly access plotly method.
import cufflinks as cf
import pandas as pd
cf.set_config_file(world_readable=True,offline=False)
pie=cf.datagen.pie()
pie.head()
pie.iplot(kind='pie',labels='labels',values='values')
df.iplot(kind='box', filename='box-plots')
df.iplot(kind='histogram', barmode='stack', bins=100, histnorm='probability', filename='histogram-binning')

matplotlib, seaborn

use existing plotting libraries, pass the handler to plotly.
import matplotlib.pyplot as plt
import numpy as np
import plotly.plotly as py
py.sign_in('jychstar', '1GPp9Dwmnsf897Z3kX8Q')
x= np.array(range(1,10))
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x,y)
plot_url = py.plot_mpl(fig)  # publish fig online
## try seaborn, found the filling area is missing
import seaborn as sns
tips = sns.load_dataset("tips")
fig, ax = plt.subplots()
ax = sns.boxplot(x="day", y="total_bill", data=tips)
plot_url = py.plot_mpl(fig)

MatLab API

Download and uncompress the Plotly MATLAB library. 210K.
cd ~/Downloads/matlab_api/
plotlysetup('jychstar', '1GPp9Dwmnsf897Z3kX8Q')
[X,Y,Z] = peaks;
contour(X,Y,Z,20);
fig2plotly()  % push fig online
getplotlyoffline('https://cdn.plot.ly/plotly-latest.min.js') % download the offline plotly bundle
fig2plotly(gcf, 'offline', true) % generate html in current working directory
% try some simple plot
x= linspace(0, 5, 101);
plot(x, sin(x.^2), '-pb', 'linewidth',2)
fig2plotly()

R API

download latest R from CRAN
I happened to have a R version 3.3.1(2016.6.21). Because R 3.3.2 was release on 2016.10.31, I guess R API was released after that.
R.version  # check, plotly require R version 3.3.2
install.packages("ggplot2")  # plotly require version >2.1.0
install.packages("plotly")
library(ggplot2)
library(plotly)
Sys.setenv("plotly_username"="jychstar")
Sys.setenv("plotly_api_key"="1GPp9Dwmnsf897Z3kX8Q")
fig = plot_ly(midwest, x = ~percollege, color = ~state, type = "box")
plotly_POST(fig, filename = "midwest-boxplots")
# try to use other plotting library other than plotly
t = seq(0,10,0.1)
fig = qplot(t,sin(t),geom="path", xlab="time", ylab="Sine wave")
plotly_POST(fig, filename = "sine_wave")

Plotly vs Xap

Plotly Xap
web-based visualization yes yes
hosting final graph data pipeline
web presentation layout one full-size graph customized
plotting tool plotly.js + naive plotting library Component + JS library
generate take-away html, png files yes no
Python, R, Matlab works in naive environment yes no
open source yes ?
business model community/advanced consulting

Monday, March 13, 2017

2nd Xap I built, AWS S3

Last mile vs first mile

The previous post showed how I built my 1st Xap. I deliberately avoided directly file import. My logical thinking is reconstructed as follows:
  1. One line of d3 code d3.csv(file, function) has already integrated 3 steps: open the local or remote file by the string name or URL, parse the file object into JSON format, callback a function to process the data.
  2. Although the first 2 steps have the corresponding Exaptive components, building a pure step-3 component could be more difficult than building a one-stop-shopping component.
  3. At that time, I hadn’t figure out what exactly is the data format during the flow between components and being sent to the dimple function.
  4. To get d3.csv work, I came out a nerdy approach: host the dataset in my own GitHub.
So my 1st Xap is focused on the data visualization, the last mile for a data scientist. “Begin with the End in Mind” is a hard lesson I learned during my past years. After that, I turned my attention to the first mile. I want to be very clear about the data format in each step and I would like to peek into every black box.

Gear up: WebStorm, AWS S3

Then I looked for a Javascript IDE, something like Jupyter notebook in Python. WebStorm caught my attention. It offers 30-day free trial and is free for 2 years for students and teachers, training and open source projects. Even for a standard individual customer, the annual fee of $59 is affordable.
After playing for a while, I realized WebStorm is good for developing the complex function of a simple input, something more pure for javascript coding. But for a web-based application, you always have to deal with file input and script input. If you don’t have a html file, the powerful libraries like jQuery and D3 are sitting on the bench because they are born to manipulate the DOM.
So I return to my previous tool set: start up a local host by python, use atom to write html/javascript codes and check the visual effect on Chrome.
Once I handcraft my html pages, I would like to have a remote host to display them. Years ago Google Drive provided such service for free. Now the hosting business is taken by AWS. I am surprised to see AWS provides up to 17 categories of services such as: compute, storage, database, developer tools, management tools, analytics, AI, Internet of things. S3 (simple static storage) is only a tiny business. By the way, the AWS page looks ugly. I guess because it is like a warehouse shopping center, the target customers care more about the price and stability, rather than a sexy face.
Here are my examples:
Note: For security reasons. the browsers block the Javascript codes. To unblock, in Chrome, there is a shield logo, click on it and “load unsafe scripts”. In firebox, there is “i” sign to fix. In Safari, sorry I don’t know how to solve it.

Papa.parse

Papa parse seems one of the most popular JavaScript libraries that do the parsing job. 4000 stars in Github. Major development around 2014. By the way, this implies it was at the time when the author, Matt Holt, was working at SmartyStreets(a company providing address info) and an undergraduate student at Brigham Young University.
I love the style of its official website: http://papaparse.com/ It uses friendly dialogues to provide case-by-case solution. So the users can quickly pin down what they want, whether it is parsing csv-format string, local file, remote file or even convert JSON back to csv. Actually, it is smart enough that it can find the right delimiter by scanning the first few rows.
var results = Papa.parse(csvString);
console.log(results.meta.delimiter);
However, there is a pitfall when parsing a local file:
Papa.parse(file, {
    complete: function(results) {
        console.log(results);}
});
According to the documentation, file is a File object obtained from the DOM. You can’t just use “data.csv” and hope it can do the magic. Papa will think “data.csv” is only a string and tell you it can’t find a delimiter. In this sense, d3 is much smarter in that d3.csv("data.csv", callback) works for local file.
Anyway, the lower-level of Papa.parse means it has more flexibility to manipulate the data stream. In a github issue discussion , Holt provides some codes that use jQuery AJAX call to pass a file:
$.get("/basic_charts/train.csv", function(text){
    var data = Papa.parse(text);
    console.log(data);
});
However, the problem by this AJAX request is that the whole file is loaded into memory. If the file is too large, the browser gets crash. Alternatively, Papa uses HTML5’s FileReader API to “stream” in the file, if a <input type="file"> element is used in the HTML file. Thanks to Raffael Vogler for providing a pain-free tutorial
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
function handleFileSelect(evt) {
    var file = evt.target.files[0];
    Papa.parse(file, {
        header: true,
        dynamicTyping: true,
        complete: function(results) {
            console.log(JSON.stringify(results, null, 2));}
    });
}
$(document).ready(function(){
    $("#csv-file").change(handleFileSelect);
});
</script>
<!--To get started we need a button to open a file: -->
<input type="file" id="csv-file" name="files"/>
console.log()is only able to print simple data formats such as string, number or array. JSON.stringify is a very powerful debugger tool that you can print the original format of an object. The only thing you can’t print is the function.
debug finding:
  1. jQeury change() is triggered by any change made to <input>, \<textara> and \<select> elements.
  2. evt is a jQuery object, has 9 top-level keys, one of them is target. Below target is an encoded JQuery file object.
  3. results has 3 top-level keys: data, errors, meta. The JSON array is under data key.
  4. add indention in stringify parameter for pretty print.
If the file is written in JSON format or JSON array,
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
        myObj = JSON.parse(this.responseText); // read raw text
        console.log(JSON.stringify(myObj));
    }
}; // define action
xmlhttp.open("GET", "data.txt", true);
xmlhttp.send();

TitanicXap2

The ready-to-use Xap is here, you can download the csv file from here). Drop the file to see the magic.
The block diagram is:
FileDropTarget:
    CSVParser:
        dimple_barplt
        Titanic_python: pie_plotly
I am going to document my source code.
For the whole Xap, the page DOM is configured in Edit-style-HTML. We can see there are three visible objects.
<div data-node="FileDropTarget_0"></div>
<div data-node="dimple_barplot_0" id="barplot"></div>
<div data-node="pie_plotly_0" id = "pie"></div>

dimple_barplot

Input: data: multiset
spect: dependencies: jQuery, d3, dimple
script: inside export default{data(){},};
let d3 = this.api.imports.d3;
let dimple = this.api.imports.dimple;
let data = this.api.inputState.export().data; // JSON Array
//this.api.log("I am Cool: ", JSON.stringify(data)); // test the raw data
function draw(data) {
  "use strict";
  var svg = dimple.newSvg("#barplot", 600, 400);  // svg inside id barplot with size (600,400)
  svg.append("text").
  attr("x", 300).attr("y", 20).attr("text-anchor", "middle").style("font-size", "20px").style("font-weight", "bold").text("Titanic Survivor");
  var myChart = new dimple.chart(svg, data);
  var x = myChart.addCategoryAxis("x", "Pclass");
  var y = myChart.addPctAxis("y", "Survived");
  var s = myChart.addSeries("Sex", dimple.plot.bar);

  s.afterDraw = function (shape, data) {
    var s = d3.select(shape),
      rect = {
        x: parseFloat(s.attr("x")),
        y: parseFloat(s.attr("y")),
        width: parseFloat(s.attr("width")),
        height: parseFloat(s.attr("height"))
      };
    if (rect.height >= 8) {
      svg.append("text")
        .attr("x", rect.x + rect.width / 2)
        .attr("y", rect.y + rect.height / 2 + 3.5)
        .style("text-anchor", "middle")
        .style("font-size", "10px")
        .style("font-family", "sans-serif")
        .style("opacity", 0.6)
        .style("pointer-events", "none")
        .text(data.yValue);
    }
  }; // end s.afterDraw
  myChart.addLegend(150, 10, 380, 20, "right");
  myChart.draw();
}// end function draw
draw(data); // call function and feed data with JSON Array format
The majority of the code is from dimple github.

Titanic_python

Input: data: dynamic
output: feature_weight: enitity
SPEC. Tricks:
  1. sklearn is based on scipy;
  2. json can’t be install by pip
  3. pandas is 0.19.2, numpy is 1.12, sklearn is 0.18
"dependencies":{
      "apt": [],
    "pip": [
      {"path": "numpy"},
      {"path": "pandas"},
      {"path": "scikit-learn"},
      {"path": "scipy"}
    ],
    "file":[]
},
script
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score,fbeta_score
import numpy as np
import sklearn
import pandas as pd

def data(self):
    data = self.api.inputstate.export()['data'] 
    # self.api.log("I am Cool: ", data) # test data
    df = pd.DataFrame(data)  # convert JSON array to dataframe
    df['Age'] = df['Age'].apply(pd.to_numeric, args=('coerce',)) # convert to number
    train_data = df

    # Data preprocessing
    # use median age to fill missing value
    def get_median_ages(df):
        median_ages = np.zeros((2,3))
        for j in range(0, 3):
            median_ages[0,j] = df[(df['Sex'] == 'female') & \
                                  (df['Pclass'] == j+1)]['Age'].dropna().median()
            median_ages[1,j] = df[(df['Sex'] == 'male') & \
                                  (df['Pclass'] == j+1)]['Age'].dropna().median()
        return median_ages
    def data_clean(df, median_ages):
        df['Gender'] = df['Sex'].map( {'female': 0, 'male': 1} ).astype(int)
        # use median age to fill the missing data
        for i in range(0, 2):
            for j in range(0, 3):
                  df.loc[ (df.Age.isnull()) & (df.Gender == i) & (df.Pclass == j+1),\
                        'Age'] = median_ages[i,j]

        droplist = ['Name','Ticket','Cabin','Embarked','Sex'] # reserve ID for check
        features = df.drop(droplist, axis = 1)
        return features

    median_ages = get_median_ages(train_data)
    train_cleaned = data_clean(train_data,median_ages)
    features = train_cleaned.drop(['PassengerId','Survived'],axis=1)
    labels = train_data ['Survived']

    # machine learning to predict a target feature: Survived
    # use train-test split to generate 2 sets of data
    X_train, X_test,y_train,y_test = train_test_split(features, labels, test_size=0.3, random_state=0)
    clf=DecisionTreeClassifier()
    clf.fit(X_train,y_train)
    pred=clf.predict(X_test)
    self.api.log("test score:",accuracy_score(y_test, pred))  # 0.81, seems good

    feature_weight={}
    for i,key in enumerate(X_train.columns.values):
        feature_weight[key] = clf.feature_importances_[i]
    self.api.output("feature_weight", feature_weight)
Note that in line 11, df = pd.DataFrame(data) convert JSON array to dataframe. This is the most important glue code that connects JavaScript to Python.

pie_plotly

Initially I tried dimple for an hour or so, but got a lot of bug. Then I switch to plotly and it was awesome!
input: data: entity
spec:
"dependencies": {
    "file": [
        {
            "type": "js",
            "path": "https://cdn.plot.ly/plotly-latest.min.js",
            "name": "Plotly"
        },
        {
            "type": "js",
            "path": "https://d3js.org/d3.v4.min.js",
            "name": "d3"
        },
        {
            "type": "js",
            "path": "https://cdnjs.cloudflare.com/ajax/libs/numeric/1.2.6/numeric.min.js",
            "name": "numeric"
        }
    ]
},
script:
export default {
    data() {
    let data = this.api.inputState.export().data;
    let d3 = this.api.imports.d3;
    let Plotly = this.api.imports.Plotly; 

    var labels = [];
    var values = [];
    var row;
    for (row in data) {        
        labels.push(String(row));
        values.push(data[row]);
    }

    var d = {}; // dictionary to store labels, values, type
    d.labels = labels;
    d.values = values;
    d.type  = "pie";

    var layout = {
      width: 500,
      height: 400,
      title : "Feature importance",
      titlefont : 20
    };

    var li = [d];
    Plotly.newPlot('pie', li, layout);
    this.api.log(labels[0]);
    }
};