High School Math Teaching & Programmer Training with Mathematica

Abby Brown
Math Teacher, Torrey Pines High School

Eric Nelson
CEO, Augment LLC

Join the Conversation #WolframTechConf

Introduction

Abby Brown    Math Teacher, Torrey Pines High School, San Diego, CA

Eric Nelson    CEO, Augment LCC, Minneapolis, MN

Alex Krotz    Student, Caltech, Pasadena, CA

Joseph Kuo    Student, University of Illinois at Champaign-Urbana, IL

Kevin Lin    Student, Torrey Pines High School, San Diego, CA

Shishir Reddy    Student, Torrey Pines High School, San Diego, CA

Acknowledgments
Thank you to Wolfram Research, San Diego State University (SDSU), and Torrey Pines High School for working together to help support our conference experience.

Background

Mathematica user since early 1990s as a student and later as teacher

Eventually students had interactive assignments and presentations

Started the Advanced Topics in Mathematics course in 2006

Highlights

Working with Wolfram community

Flexibility in creating own projects-based course

Student presentations and Annual Math Open House (5/30/2017)

Client-based projects and students’ published Demonstrations

www.MathematiClub.com/DemonstrationsList.htm

Lessons & Challenges

Don’t get committed to a lesson plan since technology changes

Technology logistics within a school district

Convincing colleagues to use Mathematica
    and those who want to learn not having enough time to learn

Making real connections for real world applications . . .

Augment Software

A phone call from Eric Nelson in Minnesota

Programmer training for students using Mathematica

Students blend skills from my classes and Augment

Learning through Collaboration - Bird Brain Game

Designer’s Intent v2.1

All functions defined in the stubs above are aggregated into a full application.
Full application is currently playable.  
It will record training activities, then store the data output in a file, which can be re-imported.  

Current functionality: 
	Early user interface for Player to study correct (canonical) color order through color pairs in {lesser, greater} format.
	User interface for Player to “test” their memory of the canonical order by asserting a “test” color pair is.. or is not... in canonical order.
	Early data export to file -- ???Export currently requires player change filepath -- Needs to be generalized.
	???Data file currently overwrites previous file in each session.  Must manually change name for file output to save additional sessions.
	Early data import.  ???Currently no visualization of data.

Pseudocode v2.1

Choose number of colors to play with.
Define colorRules... to convert numbers 1, 2, 3... to colors red, blue, green... (this becomes the “canonical” color order)
Generate list of all number pairs in correct order... i.e. {lesser, greater}.. e.g. {1,2}... NOT {2,1}... then convert this list of number pairs to color pairs using colorRules.. e.g. {1,2} -> {Red, Blue}
Show buttons to Start Training (toggle training UI to show) and Start Testing (toggle testing UI to show) and record timestamps for button clicks.
Show Training UI on click of Start Training.   Includes SlideView of all training pairs.
Show Testing UI on click of Start Testing.  Includes... 
	TestPair display
	Buttons for Player Response (i.e. True/False)
	Button to export data
	Button to import data

Code v2.2

Dynamic[colorRules] (*Comment out to remove answer key*)
Manipulate[Row[{Column[{(* Set the number of colors to be used in this BBGame session -- More colors, more difficult -- max number is 12 *)
gameNumColors =5; 

(* Define rules to convert numbers 1 through gameNumColors... defined above... to random colors..
i.e. {1 -> Red, 2 -> Blue, 3 -> Green}.. but random. *)
colorRules = colorRulesGenerator[gameNumColors];

(* Create a comprehensive list of number pairs in a {lesser, greater} format.. then uses colorRules to convert those number pairs to color pairs.
i.e. {{1,2},{1,3},{2,3}} -> {{Red, Blue},{Red, Green},{Blue, Green}} *)
trainingPairsColorsGen[gameNumColors];

(* Create toggle buttons "Start Training" and "Start Testing".
Record timestamps for when those buttons are clicked.
Toggle showTrainingPairs to initiate training and testingStart *)
(* ??? Perhaps improve name.. Stub seems to do more than name timeTraining suggests... *)
timeTraining

(* When showTrainingPairs is toggled, shows a SlideView of all training pairs. 
When training is complete, this output switches to show resultsDisplay. *)
Dynamic@If[showTrainingPairs,trainingPairsDisplay[gameNumColors]]

(* When testingStart is toggled, shows Testing UI *)
Dynamic@If[testingStart,showTestPair, ""]

}],Dynamic@BarChart[{Total@Table[correctAnswers[[n,1]],{n,1,Length[correctAnswers]}],Total@Table[incorrectAnswers[[n,1]],{n,1,Length[incorrectAnswers]}]},ChartLabels-> {"Correct","Incorrect"},ImageSize->{400,500}]}],ContentSize-> {1100,500}]

Learning through Computer Modeling

rocketoutput

Application to Business

productfordisplay

Scalable programmer training to expertise

Building a distributed network of programmers

2 Reasons:

Develop greater programmer expertise to meet software demand

Propagate engineering capabilities and better software technologies

Wolfram Mathematica as training language

Many Reasons:

High-level language -> Promotes Design over Coding
Transfer concepts from Math -> Lower cognitive load	
Semantically intuitive naming + functional programming -> Less code, Easier to read -> Collaboration
Extensible from school to career
and more...

Why Mathematica?

vs

Can access a different level of problems

vs

Augment™ Training Key Concepts & Prior Art

Engineering principles and practices

Mary Shaw Prospects for an Engineering Discipline of Software (1990)
David Parnas Software Engineering, Why and What (2013)

Expert-like learning & Assessment of understanding

Bereiter & Scardamalia Surpassing Ourselves (1993)
Wiggins & McTighe Understanding by Design (1998)
Gary Jedynak Behaviorally-Anchored Self-Assessment (2004)

Jigsawed teams & Cooperative Learning

Kim Tanner Cooperative Learning - Beyond working in groups (2003)
Harry Collins Rethinking Expertise (2007)

Scaling up programmer training

College & High School

Students develop models & simulations to support courses

Building nationally-distributed network of high school and college trainees

Jay Forrester Learning through system dynamics as preparation for the 21st Century (1994) -- [Simulations in schools]

Professionals & Business Model

Jigsawed production teams

Qualified trainees -> Apprentices -> Take lower-level work from Professionals, guided by SWE Mentors

Business model

Distributed, jigsawed team develops math-based applications for business.

E.g. What-if simulations (Small: Make or buy? Large: Propagation of different categories of errors in software development)
E.g. Data analysis, Machine Learning, Automated processes -- Industrial Internet of Things (aka Industry 4.0)

Q & A and Thank You

High School Math Teaching & Programmer Training with Mathematica

Abby Brown		abby.brown@sduhsd.net

Eric Nelson		eric@augmentsoftware.com

Alex Krotz		alexkrotz@gmail.com

Joseph Kuo		josephno1@hotmail.com

Kevin Lin		elysiumptolemus@gmail.com

Shishir Reddy		shishir_reddy@hotmail.com

Initialization

Initialization - Kevin & Shishir

1. Convert imported data into quantitative data

1.1 Import Data and Remove Skewed Columns

Designer’s Intent

Store independent and dependent variables for use in the logistic function
Remove columns containing the repeated numbers

Pseudocode

Use Import to import the data
Each column is a list for a different input variable
	e.g. BusinessTravel, Gender, DistanceFromCompany	

Define new variable listSkewedColumns
	Use Table to list all columns that consist of one value that remains the same
	Run test SameQ 
		If all the data within a column is the same, it returns True 

Define new variable cleanData
	Delete the skewed columns from the data

Show cleanData

Code

data=Import["https://community.watsonanalytics.com/wp-content/uploads/2015/03/WA_Fn-UseC_-HR-Employee-Attrition.xlsx"][[1]];
(*data=%[[1]]*)(*import the first sheet of the data containing all the values*)
(*data[[1;;5]]//TableForm (*Rows 1-5*)*) 
listSkewedColumns = (Table[And@@(SameQ[data[[2,n]],data[[#,n]]]&/@Range[2,Length[data]]),{n,1,Length@data[[1]]}]);
cleanData = Delete[data[[#]],
(Position[ listSkewedColumns,True])(*Position gives positions of functions that are skewed*)
]&/@Range[Length[data]];
(*Range for all rows of data *)

1.2 Clean Special Characters in headers

Designer’s Intent

Create a new function that can output data from any column, given the variable name

Pseudocode

Define new function findData 
	Input: name of any column
	Output: data from the column 
		(*See Alex for more information*)

Code

findData[name_,data_]:=(data[[#]]&/@Range[2,Length[data]])[[All,Flatten[Position[data[[1]],name[[#]]]&/@Range[1,Length[name]]]]]
(* Input: name of the column. Output: the corresponding column *)

deleteDuplicatesData = DeleteDuplicates[cleanData[[2;;Length[cleanData],#]] (*deletes all duplicates inside the qual data*)
]&/@Range[Length[cleanData[[1]]]];(*Range is the number of different qualitative variables *) 

cleanDataNoSpecialChars = Prepend[ (*Prepend adds the new cleaned first row back to the top*)
Drop[cleanData,{1}],StringReplace[cleanData[[1]],{"|<"->"","\\/"->"","]"->"","><"->"","_"->"","."->"",","->"","'"->""," "-> "", "&"-> "","("->"",")"->"","{"->"","}"->"","-"->""}] ];

binaryColumns = (cleanDataNoSpecialChars[[1,#]]&/@Flatten@Position[Length/@deleteDuplicatesData,2]);
(*Used to select functional variable in next section*)

binaryColumns = Delete[binaryColumns,Position[(findData[binaryColumns,cleanDataNoSpecialChars])[[1]],x_/;NumericQ[x]&&x>1 ]];

1.3 Select the Functional Variable

Designer’s Intent

Select a dependent variable
Assign a new variable name to the dependent variable’s column

Pseudocode

Select independent variable (independentVar)
Define the dependent variable’s name as a new variable functionalName

Join functionalName and its data (findData) in a new variable functionalVarColumn

Drop the functional variable from cleanData(it will be handled separately).

Code

functionalName = binaryColumns[[1]];

1.4 Dropping the Functional Variable

Designer’s Intent

Select a dependent variable
Assign a new variable name to the dependent variable’s column

Pseudocode

Select independent variable (independentVar)
Define the dependent variable’s name as a new variable functionalName

Join functionalName and its data (findData) in a new variable functionalVarColumn

Drop the functional variable from cleanData(it will be handled separately).

Code

functionalVarColumn = Join[{functionalName},Flatten@findData[{functionalName},cleanDataNoSpecialChars]];(*joins functional variable and its data*)

cleanDataNoFunctionalColumn =Drop[cleanDataNoSpecialChars//Transpose,Flatten@Position[cleanDataNoSpecialChars[[1]],functionalName] (*drop functional variable, since it's the function*)
]//Transpose;

1.5 Enter Employee Profile (UNDER DEVELOPMENT)

Designer’s Intent

Enter the employee profile with data corresponding with the original dataset

Pseudocode

UI

Code

badEmployeeProfile={35,"Travel_Frequently",700,"Research & Development",1.5,4,"Other",2069,1,"Male",60,2,1,"Healthcare Representative",1,"Divorced",5000,10000,2,"Yes",14,2,1,1,3,1,1,3,3,3,3};

goodEmployeeProfile={35,"Travel_Frequently",1324,"Sales",10,8,"Marketing",2070,4,"Male",90,4,3,"Sales Executive",4,"Married",18000,23000,2,"Yes",15,4,4,1,4,4,7,4,4,9,9};

1.6 Separate the Quantitative and Qualitative data

Designer’s Intent

Turn the numerical and qualitative data into two separate lists
	in order to convert qualitative data (words) to numbers next

Pseudocode

Create new variable numData
	Delete all qualitative data (words) and leave the numerical data

Create new variable qualData
	Delete all integers and real numbers, leaving just qualitative (non-numerical) data

Code

cleanDataWithProfile = Append[cleanDataNoFunctionalColumn,badEmployeeProfile];

numData = Delete[cleanDataWithProfile[[#]],(*Deletes all strings*)
Position[cleanDataWithProfile[[2]],_String] (*_String is part of Position function, gives list of qualitative data starting from row 2*)
]&/@ Range[Length[cleanDataWithProfile]];(*goes through all rows of data*)

badEmployeeProfileNum = Piecewise[{{{},numData=={}},{numData[[Length@numData]],numData≠{}}}];

numData = Piecewise[{{numData,numData=={}},{Drop[numData,{Length@numData}],numData≠{}}}];

qualData = (* separates qualitative data into its own list *)
Delete[cleanDataWithProfile[[#]],
Join[Position[cleanDataWithProfile[[2]],_Real],Position[cleanDataWithProfile[[2]],_Integer]](*Takes out all data that is either a real number or an integer*)
]&/@Range[Length[cleanDataWithProfile]];

1.7 Use Reference Category Coding to Convert Qualitative Data

Designer’s Intent

Convert the qualitative data into numbers
Reference category coding:
	Each column of qualitative data is comprised of n different elements 
		Ex: BusinessTravel column has 3 factors (n=3): Travel_Frequently, Travel_Rarely, Non-Travel
	(1) Create new columns for n-1 factors
		2 new columns ((3)-1) are created for 2 of the 3 factors
		Ex: Travel_Frequently and Travel_Rarely
	(2) Attach the name of the original column to each factor’s name
		Travel_Rarely-> BusinessTravel▼Travel_Rarely
		New factor columns have the same data as the original column (BusinessTravel)
	(3) Under each factor column, replace the factor name with a 1, otherwise leave the other factors as 0
		In the set of data under the Travel_Rarely column, Travel_Rarely-> 1, and Travel_Frequently-> 0, Non-Travel-> 0
		Likewise under Travel_Frequently, Travel_Frequently-> 1, and the other factors become 0
	(4) For the leftover factor without a column, it will be listed as 0 in the other factor columns 
		Since there is no Non-Travel column, Non-Travel is indicated when both Travel_Rarely-> 0 and Travel_Frequently-> 0
	(5) Delete the original column, leaving only the factor columns

Pseudocode

Define new variable deleteDuplicatesQual
	 Delete duplicate data in each column and return each set of factors as a list
	 	Use DeleteDuplicates to
		Ex: For BusinessTravel column, returns {Travel_Rarely,Travel_Frequently, Non-Travel}, followed by the next column’s set of factors
Store just the data (without variable names) from qualData as a new variable qualColumns

Define new variable qualToNumColumns to perform reference category coding 
	Using Table and Join
		Use StringJoin to set the names of each new column
		Apply rules to qualColumns to turn each factor name under its respective column into 1
		Make Table to turn the other remaining factors into 0
		Use Cases[Except[factor column]] to specify these remaining factors
			Ex: Picks out {Travel_Frequently, Non-Travel} from under the Travel-Rarely column and converts them each to 0

Code

deleteDuplicatesQual = DeleteDuplicates[qualData[[2;;Length[cleanDataWithProfile],#]] (*deletes all duplicates inside the qual data*)
]&/@Range[Length[qualData[[1]]]];(*Range is the number of different qualitative variables (e.g. BusinessTravel, Gender, MaritalStatus)*) 

qualColumns = findData[qualData[[1]],cleanDataWithProfile]; (*Reminder: findData is a function from section 1.2*)

qualToNumColumns = Flatten[ (*removes extra curly brackets*)
Table[
Join[
{StringJoin[qualData[[1,i]],"▼",deleteDuplicatesQual[[i,#]]]},(*joins original column name, ▼, and each factor column name*)

(*transforming the data into 1s and 0s*)
(*first, transform the factor that matches each factor column's name into 1*)
qualColumns[[All,i]] (*goes through all of each set of factors (i) *)
/.Join[{deleteDuplicatesQual[[i,#]]->1},(*assigns 1 to each factor under its corresponding column*) (*item (3) in Designer's Intent*)

(*next, transform everything else to 0*)
Table[
Cases[ deleteDuplicatesQual[[i]],(*goes through each set in the deleteDuplicates list*)
Except[ deleteDuplicatesQual[[i,#]]]] (*picks out all the remaining data under each factor column*)
[[n]]-> 0,{n,1,Length@Cases[deleteDuplicatesQual[[i]],Except[deleteDuplicatesQual[[i,#]]]]}]]  
(*itinerates through each part of the list of remaining factors, e.g. {Travel-Freq, Non-Travel}, and turns each of them into 0s*)

]&/@Range[Length@deleteDuplicatesQual[[i]]-1],(*# goes through each of the n-1 columns, e.g. leaving out Non-Travel *)
{i,1,Length@deleteDuplicatesQual}] (*i goes through each set of factors, e.g. BusinessTravel, Department, etc. *)
,1 (*flattens at one level*)
];

qualToNumColumns =Piecewise[{{qualToNumColumns,qualToNumColumns=={}},{Transpose[qualToNumColumns],qualToNumColumns≠{}}}];(*If there were no qualitative columns to begin with, Transpose would return an error due to evaluation on an empty list.*)

badEmployeeProfileQual = Piecewise[{{qualToNumColumns,qualToNumColumns=={}},{qualToNumColumns[[Length@qualToNumColumns]],qualToNumColumns≠{}}}];

qualToNumColumns =Piecewise[{{qualToNumColumns,qualToNumColumns=={}},{ Drop[qualToNumColumns,{Length@qualToNumColumns}],qualToNumColumns≠{}}}];

badEmployeeProfile = Join[badEmployeeProfileNum,badEmployeeProfileQual];

1.8 Clean up and Show the New Data

Designer’s Intent

Combine the now-quantitative data from 1.4 back with the numerical data
Show the new data as a table

Pseudocode

Update cleanData to Join back numerical data (numData) with the qualitative data that is now numerical (qualToNum)
	Also Join back the fuctional variable column

Update cleanData again to Prepend a cleaner version of the first row of variable names
	Remove unncessary syntax in the variable names using rules
		e.g. “_” --> “ “ (blank)

Code

cleanDataAllQuantitative = Join[ (*Join functional variable column in at the end*)
(Join[numData,qualToNumColumns,2 (*Join the new columns with numData at level 2*)
])//Transpose (*Transpose joins the data together as a new column*)
,{functionalVarColumn}]//Transpose; (*Tranpose sorts data into row by column*)

cleanDataFinalNoChars = Prepend[ (*Prepend adds the new cleaned first row back to the top*)
Drop[cleanDataAllQuantitative,{1}],StringReplace[cleanDataAllQuantitative[[1]],{"|<"->"","\\/"->"","]"->"","><"->"","_"->"","."->"",","->"","'"->""," "-> "", "&"-> "","("->"",")"->"","{"->"","}"->"","-"->""}] ];

1.9 Convert Binary Functional Variable Column into Numerical Data

Designer’s Intent

Change the functional variable data, if binary, into numbers for easier use
	Column is still in terms of Yes/No or True/False

Pseudocode

Use rules to change binary data into numbers
	Yes -> 1
	No -> 0
	True -> 1
	False -> 0

Code

cleanDataFinal =cleanDataFinalNoChars/.{"Yes"->1,"No"->0,"True"-> 1,"False"->0,"Male"->1,"Female"->0};
(*binary qualitative data is handled separately because YES and TRUE must be set to 1 in order to calculate the percent likelihood that the employee will leave. Otherwise the percent likelihood that the employee will stay would be returned*)

cleanDataFinal;

2. Create a Logistic Model

2.1 Define the Independent Variable

Designer’s Intent

Select an independent variable

Pseudocode

Select independent variable (independentVar)

Code

independentVarName = (Drop[cleanDataFinal[[1]],Flatten@Position[cleanDataFinal[[1]],functionalName]])[[1]];

2.2 Combine the Independent and Functional Variables into a Set of Ordered Pairs

Designer’s Intent

Combine functional variable data and YearsInCurrentRole data into a single data set

Pseudocode

Combine data sets independentVar and functionalVar
	Use Transpose 
		Make sure functional variable is last
	New data is in the form {years in current role, functional variable}

Code

independentVar = Flatten@findData[{independentVarName},cleanDataFinal];(*independent variable data (for logistic plot)*)

newData=findData[{"YearsAtCompany","JobInvolvement","Attrition"},cleanDataFinal];(*combines datasets with independent variable as "x" and dependent variable as "y"*)

2.3 Insert Data into Logistic Function

Designer’s Intent

Use the functional variable data stored above and turn it into a function

Pseudocode

Create logistic function using the new set of data
	Use LogitModelFit

Code

logitContour=LogitModelFit[newData,{varOne,varTwo},{varOne,varTwo}] (*create logistic function with attrition data*)

2.4 Plot the Data Points as a Logistic Function

Designer’s Intent

Visualize data from the function in a line plot

Pseudocode

Function to plot logit
	Use Plot 
	Bounds are from 0 to the maximum value of the independent variable
AxesLabel shows the variables the function is plotting

Code

newData=findData[{"YearsAtCompany","JobInvolvement","Attrition"},cleanDataFinal];

newData=findData[{"YearsAtCompany","JobInvolvement","Attrition"},cleanDataFinal];
logitContour=LogitModelFit[newData,{varOne,varTwo},{varOne,varTwo}]

orderedPairs = Drop[newData[[#]],{3}]&/@Range@Length@newData;
cfe=Function[{y},Piecewise[{{Red,y==1},{Darker[Green],y==0}}]];
newerData=Style[orderedPairs[[#]],cfe@(newData[[#,3]])]&/@Range@Length@newData;

newData=findData[{"YearsAtCompany","JobInvolvement","Attrition"},cleanDataFinal];
logitContour=LogitModelFit[newData,{x,y},{x,y}];
orderedPairs = Drop[newData[[#]],{3}]&/@Range@Length@newData;
cfe=Function[{y},Piecewise[{{Red,y==1},{Darker[Green],y==0}}]];
newerData=Style[orderedPairs[[#]],cfe@(newData[[#,3]])]&/@Range@Length@newData;
ContourPlot[logitContour[x,y],{x,0,40},{y,0,4}]

ListPointPlot3D[Table[logitContour[x,y],{x,0,40},{y,0,4}]]

2.5 Add Other Input Variables

Designer’s Intent

Adjust the program to include an unlimited number of inputs (instead of just one or two)
	For data without numerical inputs, 
		Change the data into numbers

Pseudocode

Create new variable newData to put all the variables together

Insert all variables into logistic model 
	Use LogitModelFit

Test the model using sample employee profiles

Code

newData = Drop[cleanDataFinal,{1}];
(* Drops the first row with all the column names *)
logitFull =LogitModelFit[newData,Symbol/@Flatten@Drop[cleanDataFinal[[1]],Flatten@Position[cleanDataFinal[[1]],functionalName]], Symbol/@Flatten@Drop[cleanDataFinal[[1]],Flatten@Position[cleanDataFinal[[1]],functionalName]]] (*add all non-skewed variables into logit function*)

logitFull@@badEmployeeProfile

(*testing the model*)

(*logit[35,700,5,10,2069,1,60,2,1,2,2000,10000,1,7,2,1,1,2,2,1,2,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1] 
(*Bad employee profile: *)*)

(*logit[35,1324,5,8,2070,4,90,4,3,4,18000,23000,1,18,4,4,1,7,4,4,9,3,3,3,0,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1] 
(*Good employee profile: *)*)

3. Analyze the Data

3.1 Determine the Statistical Significance

Designer’s Intent

Find 
	Variables that are statistically significant
	The level of impact on functional variable
	Whether they positively or negatively impact functional variable
Determine statistics  
	Estimate
	Standard Error
	z-Statistic 
	p-value

Pseudocode

Use the ParameterTable entry in the LogitFunction

Code

logitFull["ParameterTable"]; (*legend: 
P-Value -> Determines statistical significance. As long as the value is less than 0.05, the variable is considered statistically significant, but the closer to 0 the better
z-Statistic -> Shows magnitude of the correlation between variable and function (attrition). Variable with the greatest absolute value of the z-Statistic has the greatest impact on attrition
Standard Error -> Standard Error of regression coefficient
Estimate -> Also known as regression coefficient. Sign of Estimate determines whether the variable has a positive or negative impact on attrition. In this case,  a positive value increases the chance of attrition, or the likelihood that the employee will leave the company*)

tableEntries = Drop[logitFull["ParameterTableEntries"],{1}];
(*each row from the table is turned into a list. First row, the intercept, is dropped because it is not used for correlation*)

reliableData = Join[(*Joins the following two lists together to form a new set of lists*) (*Variable names (Age, DailyRate, etc.) become the first column now*)
(*first list*)
cleanDataFinal[[1,#]](*gives all the variable names (row 1) that have p-values less than 0.05*)
&/@ Position[tableEntries[[All,4]],x_/;x<0.05],(*goes through the fourth column (p-value) in all the rows of the ParameterTable, and returns the position of rows with p-value>0.05*)
(*second list*)

Delete[tableEntries,Position[tableEntries[[All,4]],x_/;x>0.05]](*finds position of rows with p-value>0.05, and deletes these rows from tableEntries*)
,2];(*joins data at the second level*)

sortedReliableData = SortBy[(*default sorts by least to greatest*)
reliableData,
-Abs@#[[4]]&];(*sorts by absolute numerical data in the 4th column (z-value) using Abs*)(*abs value of z value will give the magnitude of correlation*)

groups = GatherBy[sortedReliableData,Positive@#[[2]]&];

cf=Function[{y},Piecewise[{{Red,y>0},{Darker[Green],y<0}}]];

orderedColorSignificance=Style[#[[1]],cf@(#[[2]])]&/@sortedReliableData;

styleTable = Grid[
(*add column labels*)Prepend[
(*pair up attribute & significance*){#,Position[orderedColorSignificance,#][[1,1]]}&/@
orderedColorSignificance[[1;;5]],
{"Variable","Significance"}],
(*making the table look more organized*)Dividers->{2->Black,2->Black},Spacings->{1,.5},Alignment->{{Right,Left},Center}
]

styleTableNegative = Grid[
(*add column labels*)Prepend[
(*pair up attribute & significance*){Style[top5PositiveImpact[[#,1]],Red],#}&/@
Range@Length@top5PositiveImpact,
{"Variable","Positive Significance"}],
(*making the table look more organized*)Dividers->{2->Black,2->Black},Spacings->{1,.5},Alignment->{{Right,Left},Center}
];

styleTablePositive = Grid[
(*add column labels*)Prepend[
(*pair up attribute & significance*){Style[top5NegativeImpact[[#,1]],Darker[Green]],#}&/@
Range@Length@top5NegativeImpact,
{"Variable","Positive Significance"}],
(*making the table look more organized*)Dividers->{2->Black,2->Black},Spacings->{1,.5},Alignment->{{Right,Left},Center}
];

contourVariableOne = (reliableData//Transpose)[[1,1]];
contourVariableTwo = (reliableData//Transpose)[[1,2]];

positiveEstimate=Piecewise[{{groups[[1]],groups[[1,1,2]]>0},{{},And@@{Length@groups==1,groups[[1,1,2]]<0}},{groups[[2]],groups[[2,1,2]]>0}}];

negativeEstimate=Piecewise[{{groups[[1]],groups[[1,1,2]]<0},{groups[[2]],(Length@groups>1)&&groups[[2,1,2]]<0},{{},Length@groups==1&&groups[[1,1,2]]>0}}];

Style[Grid[Pick[sortedReliableData,Positive@sortedReliableData[[All,2]]],Alignment->Right],Red]
Style[Grid[Pick[sortedReliableData,Negative@sortedReliableData[[All,2]]],Alignment->Right],Darker[Green]]

top5PositiveImpact = Piecewise[{{positiveEstimate[[1;;5]],Length@positiveEstimate≥5},{positiveEstimate,Length@positiveEstimate≤5}}];

top5NegativeImpact = Piecewise[{{negativeEstimate[[1;;5]],Length@negativeEstimate≥5},{negativeEstimate,Length@negativeEstimate≤5}}];

3.2 Select Variable to be Graphed

Designer’s Intent

Select the variable to be graphed in the bubble chart alongside the functional variable
	Choose from the statistically significant variables

Pseudocode

Create a ActionMenu with a list of choices contatining only statistically significant variables

Code

independentName = orderedColorSignificance[[1]];

3.3 Visualize the Data with BubbleChart

Designer’s Intent

Better visualize an variable’s impact on attrition using a graph
	Use BubbleChart 
		Plots average value of the functional variable over any independent variable
	Chart also shows which time group hold more weight
		(i.e. a bigger circle means more people are in that group, so that group holds more weight)

Pseudocode

Define new variable pairs - a list of ordered pairs
	Transpose combines functional variable (y-value) and any input variable into an ordered pair

Define new variable meanvalues
	Take the mean value of attrition for each time group 
		Use Mean 
	Group the pairs by each time group (e.g. one year, two years, etc.) 
		Use GroupBy 
		Returns list of ordered pairs {independent variable, funcitonal variable}

Define new variable sizes 
	Assigns each time group a number corresponding to the amount of people in that group
		Visualizes which groups hold more weight based on the magnitude of sizes

Separate the list from meanvalues into just its x and y components
	Define new variable meanvaluesx -- time in company
	Define new variable meanvaluesy -- mean value of attrition

Combine meanvaluesx, meanvaluesy, and sizes
	Use Transpose  
 Graph the data visualization
 	Use BubbleChart

Code

pairs = findData[{ToString@independentName,functionalName},cleanDataFinal]; (* YearsAtCompany vs. Attrition *)
domain = Length[DeleteDuplicates[findData[{ToString@independentName},cleanDataFinal]]];(* Number of distinct values in the YearsAtCompany column *)
meanvalues = Mean[ (* Takes the mean of the list of groups created by GroupBy *)
GroupBy[pairs,First][[#]] (* GroupBy is a rule that sorts all the pairs according to the first element (time), e.g. all the 1-years, 2-years, etc. *)
]&/@Range[ (* Maps each value over the domain of all the time groups*) 
domain];
sizes = (Length[GroupBy[pairs,First][[#]]] )(* GroupBy is a rule that sorts all the pairs according to the first element (time), e.g. all the 1-years, 2-years, etc. *)&/@Range[domain];
(* &/@ maps through each group in the list, giving the length of each group *)
meanvaluesx = meanvalues[[All,1]];
meanvaluesy = meanvalues[[All,2]];
newdata = Transpose[{meanvaluesx,meanvaluesy,sizes}]; (*Combines into a list of ordered triples*)
(*BubbleChart requires 3-dimensional list (ordered triples) for an input*)
bubbleChart = BubbleChart[newdata,BubbleSizes->{.01,0.06}, (* BubbleSizes sets the appropriate size for the bubbles *)
ChartStyle->{"Rainbow"},PlotTheme->"Business",FrameLabel->{independentName,StringJoin["Average ",functionalName]},PlotLabel->StringJoin["Average ",functionalName," vs. ",ToString@independentName],ImageSize->Large,Frame->True,FrameTicks->Automatic,BaseStyle->{FontSize->14}]

4. Beta Applications

4.1 Test Employee Profile (Implementing UI still, but test works)

(*Bad Employee Profile: {35,"Travel_Frequently",700,"Research & Development",1.5,4,"Other",2069,1,"Male",60,2,1,"Healthcare Representative",1,"Divorced",5000,10000,2,"Yes",14,2,1,1,3,1,1,3,3,3,3}, bad work life balance, divorced, low job satisfaction, low income, little work experience*)

(*What is the percent likelihood that this employee will leave the company?*)
logitFull@@badEmployeeProfile

End Result

productfordisplay:=Manipulate[Switch[tabNumber,
"Logistic Model",
(functionalVarColumn = Join[{functionalName},Flatten@findData[{functionalName},cleanDataNoSpecialChars]];
cleanDataNoFunctionalColumn =Drop[cleanDataNoSpecialChars//Transpose,Flatten@Position[cleanDataNoSpecialChars[[1]],functionalName]
]//Transpose;badEmployeeProfile={35,"Travel_Frequently",700,"Research & Development",1.5,4,"Other",2069,1,"Male",60,2,1,"Healthcare Representative",1,"Divorced",5000,10000,2,"Yes",14,2,1,1,3,1,1,3,3,3,3};cleanDataWithProfile = Append[cleanDataNoFunctionalColumn,badEmployeeProfile];
numData = Delete[cleanDataWithProfile[[#]],
Position[cleanDataWithProfile[[2]],_String]
]&/@ Range[Length[cleanDataWithProfile]];
badEmployeeProfileNum = Piecewise[{{{},numData=={}},{numData[[Length@numData]],numData≠{}}}];
numData = Piecewise[{{numData,numData=={}},{Drop[numData,{Length@numData}],numData≠{}}}];
qualData =
Delete[cleanDataWithProfile[[#]],
Join[Position[cleanDataWithProfile[[2]],_Real],Position[cleanDataWithProfile[[2]],_Integer]]]&/@Range[Length[cleanDataWithProfile]];deleteDuplicatesQual = DeleteDuplicates[qualData[[2;;Length[cleanDataWithProfile],#]]]&/@Range[Length[qualData[[1]]]];
qualColumns = findData[qualData[[1]],cleanDataWithProfile]; 
qualToNumColumns =Flatten[Table[Join[{StringJoin[qualData[[1,i]],"▼",deleteDuplicatesQual[[i,#]]]},
qualColumns[[All,i]] /.Join[{deleteDuplicatesQual[[i,#]]->1},Table[Cases[ deleteDuplicatesQual[[i]],Except[ deleteDuplicatesQual[[i,#]]]]
[[n]]-> 0,{n,1,Length@Cases[deleteDuplicatesQual[[i]],Except[deleteDuplicatesQual[[i,#]]]]}]]]&/@Range[Length@deleteDuplicatesQual[[i]]-1],
{i,1,Length@deleteDuplicatesQual}],1];
qualToNumColumns =Piecewise[{{qualToNumColumns,qualToNumColumns=={}},{Transpose[qualToNumColumns],qualToNumColumns≠{}}}];
badEmployeeProfileQual = Piecewise[{{qualToNumColumns,qualToNumColumns=={}},{qualToNumColumns[[Length@qualToNumColumns]],qualToNumColumns≠{}}}];
qualToNumColumns =Piecewise[{{qualToNumColumns,qualToNumColumns=={}},{ Drop[qualToNumColumns,{Length@qualToNumColumns}],qualToNumColumns≠{}}}];
badEmployeeProfile = Join[badEmployeeProfileNum,badEmployeeProfileQual];cleanDataAllQuantitative = Join[(Join[numData,qualToNumColumns,2 ])//Transpose,{functionalVarColumn}]//Transpose;
cleanDataFinalNoChars = Prepend[Drop[cleanDataAllQuantitative,{1}],StringReplace[cleanDataAllQuantitative[[1]],{"|<"->"","\\/"->"","]"->"","><"->"","_"->"","."->"",","->"","'"->""," "-> "", "&"-> "","("->"",")"->"","{"->"","}"->"","-"->""}] ];cleanDataFinal =cleanDataFinalNoChars/.{"Yes"->1,"No"->0,"True"-> 1,"False"->0,"Male"->1,"Female"->0};newData = Drop[cleanDataFinal,{1}];
logitFull =LogitModelFit[newData,Symbol/@Flatten@Drop[cleanDataFinal[[1]],Flatten@Position[cleanDataFinal[[1]],functionalName]], Symbol/@Flatten@Drop[cleanDataFinal[[1]],Flatten@Position[cleanDataFinal[[1]],functionalName]]]),

"Most Impactful Factors",
(tableEntries = Drop[logitFull["ParameterTableEntries"],{1}];
reliableData = Join[cleanDataFinal[[1,#]]&/@ Position[tableEntries[[All,4]],x_/;x<0.05],Delete[tableEntries,Position[tableEntries[[All,4]],x_/;x>0.05]],2];
sortedReliableData = SortBy[reliableData,-Abs@#[[4]]&];
groups = GatherBy[sortedReliableData,Positive@#[[2]]&];
cf=Function[{y},Piecewise[{{Red,y>0},{Darker[Green],y<0}}]];
orderedColorSignificance=Style[#[[1]],cf@(#[[2]])]&/@sortedReliableData;
styleTable = Grid[Prepend[{#,Position[orderedColorSignificance,#][[1,1]]}&/@orderedColorSignificance[[1;;5]],{"Variable","Significance"}],Dividers->{2->Black,2->Black},Spacings->{1,.5},Alignment->{{Right,Left},Center}]),

"2D Plot",
(independentVarName = indVar;independentVar = Flatten@findData[{independentVarName},cleanDataFinal];
newData=findData[{independentVarName,functionalName},cleanDataFinal];logit=LogitModelFit[newData,varOne,varOne];
Plot[logit[x],{x,0,Max@DeleteDuplicates@independentVar},AxesLabel->{independentVarName,functionalName},ImageSize-> Large]),

"Bubble Chart",
(independentName = bubbleChartVar;pairs = findData[{ToString@independentName,functionalName},cleanDataFinal]; domain = Length[DeleteDuplicates[findData[{ToString@independentName},cleanDataFinal]]];meanvalues = Mean[GroupBy[pairs,First][[#]]]&/@Range[domain];sizes = (Length[GroupBy[pairs,First][[#]]] )&/@Range[domain];
meanvaluesx = meanvalues[[All,1]];
meanvaluesy = meanvalues[[All,2]];newdata = Transpose[{meanvaluesx,meanvaluesy,sizes}]; 
BubbleChart[newdata,BubbleSizes->{.01,0.06}, 
ChartStyle->{"Rainbow"},PlotTheme->"Business",FrameLabel->{independentName,StringJoin["Average ",functionalName]},PlotLabel->StringJoin["Average ",functionalName," vs. ",ToString@independentName],ImageSize->Large,Frame->True,FrameTicks->Automatic,BaseStyle->{FontSize->14}])],

{{tabNumber,"Most Impactful Factors","Options"},{"Logistic Model","Most Impactful Factors","2D Plot","Bubble Chart"}},

{{funcVar,functionalName,"Functional Variable"},binaryColumns,PopupMenu},
PaneSelector[
{"2D Plot"->Control[{{indVar,independentVarName,"2D Plot Variable"},(Drop[cleanDataFinal[[1]],Flatten@Position[cleanDataFinal[[1]],functionalName]]),PopupMenu}],
"Bubble Chart"->Control[{{bubbleChartVar, independentName, "Bubble Chart Variable"},orderedColorSignificance,PopupMenu}]},tabNumber],

ControlPlacement->Top,SaveDefinitions->True,

Bookmarks->{"BubbleChartYWCM":>{bubbleChartVar="YearsWithCurrManager",funcVar="Attrition",indVar="Age",tabNumber="Bubble Chart",variableOne="Age",variableTwo="DistanceFromHome"},"BubbleChartYICR":>{bubbleChartVar="YearsInCurrentRole",funcVar="Attrition",indVar="Age",tabNumber="Bubble Chart",variableOne="Age",variableTwo="DistanceFromHome"},"BubbleChartYAC":>{bubbleChartVar="YearsAtCompany",funcVar="Attrition",indVar="Age",tabNumber="Bubble Chart",variableOne="Age",variableTwo="DistanceFromHome"},JobRole-Research:>{bubbleChartVar="DistanceFromHome",funcVar="Attrition",indVar="JobRole▼ResearchScientist",tabNumber="2D Plot"},Bubble-Distance:>{bubbleChartVar="DistanceFromHome",funcVar="Attrition",indVar="JobRole▼ResearchScientist",tabNumber="Bubble Chart"}}];

Initialization - Alex

rocketMass=110;
fuelMass=30;
burnRate=.1;
burnSpeed=15000;
burnTime=fuelMass/burnRate;
thrust=burnSpeed*burnRate;
totalMass=rocketMass+fuelMass;

acceleration1:=(thrust)/(fuelMass+rocketMass)-10
NoMassLoss[t_]:=1/2*acceleration1*t^2
NoMassLossVelocity[t_]:=acceleration1*t

positionVariableMass[t_]:=burnSpeed t-5(t^2)-(burnSpeed/burnRate)(totalMass-burnRate t)Log[totalMass/(totalMass-burnRate t)]

item[time_]:=Row[{
Labeled[Style["Rocket Without Loss of Mass",FontSize->25],
Column[{
Labeled[Style["x(t)",FontSize->20],
Panel@ParametricPlot[{t,NoMassLoss[t]},{t,0,time},PlotRange->{{0,burnTime},{0,75000}},AspectRatio->Full,AxesLabel->{t,m},LabelStyle->Directive[Bold, Medium],ImageSize->{400,500},PlotStyle->Thickness[0.01]],
BaselinePosition->Top],
Column[{
(*Labeled[Style["Velocity",FontSize->20],
Panel[Plot[NoMassLossVelocity[t],{t,0,burnTime},PlotRange->{0,600},AxesLabel->{t,v},LabelStyle->Directive[Bold, Medium]]],ImageSize->Medium],*)
Panel[Style["(Subscript[F, Net]) = (Subscript[V, Exhaust])((\[DifferentialD]m)/(\[DifferentialD]t)) - (Subscript[m, 0])g"//TraditionalForm,FontSize->23],Alignment->{Center,Center},ImageSize->{375,60},Appearance->"Frameless",Background->White]
},BaselinePosition->Top]
},"  "],Spacings->{0,1}],
Labeled[Style["Rocket Accounting for Loss of Mass",FontSize->25],
Column[{
Labeled[Style["x(t)",FontSize->20],
Panel@ParametricPlot[{t,positionVariableMass[t]},{t,0,time},PlotRange->{{0,burnTime},{0,75000}},AspectRatio->Full,AxesLabel->{t,m},LabelStyle->Directive[Bold, Medium],ImageSize->{400,500},PlotStyle->Thickness[0.01]],
BaselinePosition->Top],
Column[{(*
Labeled[Style["Velocity",FontSize->20],
Panel[Plot[burnSpeed*Log[totalMass/(totalMass-burnRate*t)]-10t,{t,0,burnTime},AxesLabel->{t,v},LabelStyle->Directive[Bold, Medium]]],ImageSize->Medium],*)
Panel[Style[TraditionalForm["(Subscript[F, Net]) = (Subscript[V, Exhaust])((\[DifferentialD]m)/(\[DifferentialD]t)) - m(t)(((\[DifferentialD]v)/(\[DifferentialD]t)) + g)"],FontSize->23],Alignment->{Center,Center},ImageSize->{375,60},Appearance->"Frameless",Background->White]
},BaselinePosition->Top]
},"  "],Spacings->{0,1}]
},"                                        "];

rocketoutput:=Labeled[Style["Question: A rocket of mass 140kg, 30kg being fuel, accelerates for 300 seconds. It burns 
fuel at a rate of 1kg/s, ejecting it at 15,000m/s. Plot the position of the rocket.",FontSize->25,FontFamily->"Arial"],
Manipulate[item[Time],{{Time,0.00001,Style["Time",20]},0.00001,burnTime,ImageSize->Large}],Spacings->{0,1}];

Initialization - Joseph

Clear[correctAnswers,incorrectAnswers];
testPairList={};
(* Function to generate a list of rules based on randomized color list *)
colorRulesGenerator[numColors_]:=(
colorList = {Red,Yellow, Green, Blue, Black, Orange, Cyan, Magenta, Brown, White, Purple, Pink};
(* Initialize number of colors to play game with *)
gameNumColors = numColors;
(* Randomize a sublist of n colors from colorList, n = gameNumColors *)
colorListRandom = RandomSample[colorList[[1;;numColors]]];
Table[i-> colorListRandom[[i]], {i,numColors}]
)
trainingPairsColorsGen[gameNumColors_]:=RandomSample@(* Randomize the Table of Training Pairs *)
Partition[
Flatten[ (* formats output as a list of pairs *)
Table[ (* Scrolls through i, from 1 to gameNumColors-1 *)
Table[{i,j},{j,i+1,gameNumColors}] (* Generates Training Pair {i, j}, where j goes from i+1 to n *)
,{i,gameNumColors-1}]
],2]/.colorRules
trainingPairsDisplay[gameNumColors_]:=(
trainingPairsColors=trainingPairsColorsGen[gameNumColors];
(*sets list of color pairs with a specific number of colors*)
SlideView[Map[Graphics[{
trainingPairsColors[[#,1]],Rectangle[{0,0},{1,1}],
trainingPairsColors[[#,2]],Rectangle[{1,0},{2,1}]},ImageSize->400]&,Range[Length[trainingPairsColors]]],AppearanceElements->{"PreviousSlide","NextSlide","SlideTotal"}]
)
timeTraining:=(
(*Initalization*)
Clear[trainingStartTime,trainingEndTime, testingStartTime,testingEndTime];
trainingStart=False;
trainingEnd = False;
testingStart=False;
testingEnd = False;
showTrainingPairs=False;

Column[{
Dynamic[(*Start Training button -> On-click becomes "Training In-Progress" -> When Start Testing clicked becomes "Training Complete" *)
If[trainingStart ===True ,
If[testingStart===False,
"Training In-Progress",
"Training Complete"
],
If[trainingEnd === False,
Button["Start Training",{
trainingStart=True,
trainingStartTime=Now, (* Records Training Start Time *)
showTrainingPairs=True}(* Turns TrainingPair SlideView on *)
],
"Training Complete"
]
]],
Dynamic[(*Start Testing button -> On-click becomes "Testing In-Progress", Sets "Training In-Progress" to "Training Complete" *)
If[trainingStart ===True ,
If[testingStart===False,
Button["Start Testing",
{trainingEnd = True,
trainingEndTime=Now, (* Records Training End Time *)
testingStart=True, 
correctAnswers = {},
incorrectAnswers = {},
testingStartTime=Now,(* Records Testing Start Time *)
showTrainingPairs=False}(* Turns TrainingPair SlideView off *)
],
"Testing In-Progress"
]
]],
Dynamic[If[testingStart,timeSpentTraining]]
}]
)
genTestPair:=(
firstRandom=RandomInteger[{1,gameNumColors}];
(*excluding firstRandom to avoid repeating colors*)
secondRandomChoices=DeleteCases[Range[gameNumColors],firstRandom];
secondRandom=RandomChoice[secondRandomChoices];
testPair={firstRandom,secondRandom};
testPairColor={firstRandom,secondRandom}/.colorRules;
Graphics[{
(* pairSelect scrolls through each Training Pair in trainingPairColors *)
testPairColor[[1]],Rectangle[{0,0},{1,1}],
testPairColor[[2]],Rectangle[{1,0},{2,1}]
},ImageSize->400])
checkAnswer := (
testPairOrder:=testPair[[1]]<testPair[[2]];
If[testResponse===testPairOrder,
AppendTo[correctAnswers,{1,testPair,Now}],
AppendTo[incorrectAnswers,{1,testPair,Now}]
]
genTestPair 
)
showTestPair :=Column[{genTestPair,
Button["True",{testResponse=True, checkAnswer,AppendTo[testPairList,testPair]}],
Button["False",{testResponse=False,checkAnswer,AppendTo[testPairList,testPair]}],
Button["Finished",{testingEndTime=Now,{totaldata}>>>"BirdBrainDataArray.txt",correctAnswers={},incorrectAnswers= {}}], (* Player must change file path to desired location *)
Button["Import Data", {testImport = Get["BirdBrainDataArray.txt"]}]
}]
SetDirectory[NotebookDirectory[]];
resultsDisplay :=Column[
{
StringRiffle[{Total[correctAnswers[[All,1]]],"Correct"}], (* Outputs a string with total Correct *)
StringRiffle[{Total[incorrectAnswers[[All,1]]],"Incorrect"}](* Outputs a string with total Incorrect *)
}
]
answerTimesPlot:=(answerTimestamps=testImport[[1,5,All,3]];
timestampDiffs=Table[UnitConvert[answerTimestamps[[i+1]]-answerTimestamps[[i]],"Seconds"],{i,Length[answerTimestamps]-1}];
ListLinePlot[timestampDiffsTooltips,PlotMarkers->{●, 28},ImageSize->Large,PlotLabel->"Time taken for each answer (in seconds)",AxesLabel->Automatic,PlotMarkers->testPairSequence[[1,All]]])
testPairSequence:=testImport[[1,7]]/.colorRules
pairsWithTooltips:=Partition[Riffle[timestampDiffs,testPairSequence],2]
timestampDiffsTooltips:=Table[Tooltip[pairsWithTooltips[[i,1]],Row[pairsWithTooltips[[i,2]]]],{i,Length[pairsWithTooltips]}];

Start Space

2+2