Created
August 8, 2017 00:35
-
-
Save bdiesel/9021673ce0158ff853ac1e29f5779314 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
| let tableData = require('./dataset_test.json'); | |
| //let tableData = require('./100k.json'); | |
| let removeResiduals = true; | |
| // Primary segmentation (One row for each combination) | |
| // (36x12x5=2,160 possible combinations; approx 220 used) | |
| // segments are AND together | |
| // user can define composite variables that combine separate variables | |
| // (e.g. ‘high ticket buyer and outside 25 mile radius of store’ as one row) | |
| let filterArray = { | |
| //each segment can be multiple key value pairs | |
| segments: [{ | |
| keys: ["primary_buyer_group", "primary_recency_ranges"], | |
| //values: ["MICROSITES EXCEPT", "OPENBOX.COM"] // OR these | |
| values: [ | |
| ["AMAZON", "OPENBOX.COM", "MICROSITES EXCEPT"], | |
| ["37-48"] | |
| ] | |
| }, | |
| { | |
| keys: ["primary_score_range"], // AND these | |
| values: [ | |
| ["Direct Seg 15-20"] | |
| ] | |
| }, | |
| { | |
| keys: ["primary_score_range"], // AND these | |
| values: [ | |
| ["Direct Seg 1-4"] | |
| ] | |
| }, { | |
| keys: ["primary_buyer_group"], | |
| values: [ | |
| ["WISH LIST"] | |
| ] | |
| }, { | |
| keys: ["primary_recency_ranges"], | |
| values: [ | |
| ["13-24"] | |
| ] | |
| } | |
| ], | |
| // Each split is an additional filter on all rows in the matrix. | |
| // Customers are assigned to first split for which they qualify, starting from the left | |
| splits: [{ | |
| keys: ["split_buyer_group_1"], | |
| values: [ | |
| ["MULTIES"] | |
| ] | |
| }, | |
| { | |
| keys: ["split_locations"], | |
| values: [ | |
| ["NON-RADIUS (Non-Store Area)"] | |
| ] | |
| }, | |
| { | |
| keys: ["split_buyer_group_2"], | |
| values: [ | |
| ["Browse Only - 0-6 Mo"] | |
| ] | |
| }, | |
| { | |
| keys: ["split_zip_match"], | |
| values: [ | |
| ["Version 4 - 50 Mile Radius zip Matches - Rekey"] | |
| ] | |
| }, | |
| { | |
| keys: ["split_zip_match"], | |
| values: [ | |
| ["Version 4 - 50 Mile Radius zip Matches - Rekey"] | |
| ] | |
| }, | |
| { | |
| keys: ["split_style"], | |
| values: [ | |
| ["LP - Modern"] | |
| ] | |
| } | |
| ] | |
| }; | |
| class SelectionMatrix { | |
| constructor(data, filters, removeResiduals) { | |
| this.tableData = data; | |
| this.filterArray = filters; | |
| this.removeResiduals = removeResiduals; | |
| } | |
| generateMatrix() { | |
| //stored for output | |
| let segArray = []; | |
| // Creates a copy of tableData array and itterates over the table | |
| // by the segmenet removing the appropiate elements. | |
| this.filterArray.segments.reduce(function(cur, seg) { | |
| //get the segement ad an array | |
| let segDataArray = this.getSegments(cur, seg); | |
| //get the split as object {total: int, splits: []} | |
| let mySplits = this.getSplits(segDataArray, this.filterArray.splits); | |
| //elements to remove after each segment/row pass | |
| let deleteList = []; | |
| if (this.removeResiduals) { | |
| deleteList = segDataArray; | |
| } else { | |
| //flatten splits 2d array to 1d array | |
| deleteList = mySplits.splits.reduce((a, b) => a.concat(b), []); | |
| } | |
| //Add the segment to the output | |
| segArray.push({ | |
| allSegments: segDataArray, | |
| splits: mySplits.splits, | |
| residuals: mySplits.residuals | |
| }); | |
| //this is a 2d passed back to the reduce element. | |
| return cur.filter(el => !deleteList.includes(el)); | |
| }.bind(this), this.tableData); | |
| //we return all the elements and do the counting later. | |
| return segArray; | |
| }; | |
| // This is used to render the oput of the table. | |
| drawCounts() { | |
| let matrix = this.generateMatrix(); | |
| let rows = []; | |
| let num_cols = matrix[0].splits.length; | |
| let splitsColTotals = new Array(num_cols).fill(0); | |
| let colResids = new Array(num_cols).fill(0); | |
| let allSelectionTotal = 0 | |
| matrix.forEach(row => { | |
| let splitsLen = []; | |
| let splitTotal = 0; | |
| row.splits.forEach((col, idx) => { | |
| splitsLen.push(col.length); | |
| splitTotal += col.length; | |
| allSelectionTotal += col.length; | |
| splitsColTotals[idx] += col.length; | |
| }); | |
| rows.push({ | |
| total: row.allSegments.length, | |
| splits: splitsLen, | |
| splitsTotal: splitTotal, | |
| residuals: row.residuals.length | |
| }); | |
| }); | |
| return { | |
| rows: rows, | |
| splits_col_total: splitsColTotals, | |
| all_selections_total: allSelectionTotal, | |
| column_residuals: colResids | |
| }; | |
| }; | |
| // This is used to generate the rows of the table. | |
| getSegments(matrix, segmentGroup) { | |
| return segmentGroup.keys.reduce(function(cur, keyVal, idx) { | |
| let rowBucket = []; //rolls up the row row buckets incase | |
| cur = segmentGroup.values[idx].reduce(function(res, val) { | |
| res = cur.filter(el => el[keyVal] == val); | |
| rowBucket.push.apply(rowBucket, res); | |
| return rowBucket; | |
| }, []); | |
| return cur; | |
| }, matrix); | |
| }; | |
| getSplits(segData, splitsFilter) { | |
| let segSplits = []; | |
| splitsFilter.forEach(splitFilter => { | |
| let split = splitFilter.keys.reduce(function(cur, keyVal, idx) { | |
| let colBucket = []; //rolls up the col buckets incase | |
| cur = splitFilter.values[idx].reduce(function(res, val) { | |
| res = cur.filter(el => el[keyVal] == val); | |
| //remove element from row so it can't be counted twice. | |
| segData = segData.filter(el => !res.includes(el)); | |
| colBucket.push.apply(colBucket, res); | |
| return colBucket; | |
| }, []); | |
| return cur; | |
| }, segData); | |
| segSplits.push(split); | |
| }); | |
| //the remaining are residuals. | |
| return { splits: segSplits, residuals: segData }; | |
| } | |
| } // End SelectionMatrix class | |
| //let runningTime = "Not Calculated"; | |
| let runningTime = process.hrtime(); | |
| let dataMatrix = new SelectionMatrix(tableData, filterArray, removeResiduals) | |
| let counts = dataMatrix.drawCounts(); | |
| runningTime = process.hrtime(runningTime); | |
| console.log("Output Matrix:", counts, "Running Time:", runningTime); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment