.get(0) in a JS machine learning Algo Don't work - javascript

I'm on my way to create my first KNN Algo to learn machine learning.
I'm looking after a basic course online that's explaining it, I'm feeling that I did exactly the same as he did.
But when I'm running it I get this pretty basic error of js.
I am using TensorFlow.
.sort((a, b) => (a.get(0) > b.get(0) ? 1 : -1))
^
TypeError: a.get is not a function
require('#tensorflow/tfjs-node');
const tf = require('#tensorflow/tfjs');
const loadCSV = require('./load-csv');
function knn(features, labels, predictionPoint, k) {
return (
features
.sub(predictionPoint)
.pow(2)
.sum(1)
.pow(0.5)
.expandDims(1)
.concat(labels, 1)
.unstack()
.sort((a, b) => (a.get(0) > b.get(0) ? 1 : -1))
.slice(0, k)
.reduce((acc, pair) => acc + pair.get(1), 0) / k
);
}
let { features, labels, testFeatures, testLabels } = loadCSV(
'kc_house_data.csv',
{
shuffle: true,
splitTest: 10,
dataColumns: ['lat', 'long'],
labelColumns: ['price'],
}
);
features = tf.tensor(features);
labels = tf.tensor(labels);
console.log(features, labels, tf.tensor(testFeatures[0]), 10);
const result = knn(features, labels, tf.tensor(testFeatures[0]), 10);
console.log('Guess', result, testLabels[0][0]);
console.log(features);
the log on the top to see whats passing in the function.
Tensor {
kept: false,
isDisposedInternal: false,
shape: [ 21602, 2 ],
dtype: 'float32',
size: 43204,
strides: [ 2 ],
dataId: { id: 0 },
id: 0,
rankType: '2'
} Tensor {
kept: false,
isDisposedInternal: false,
shape: [ 21602, 1 ],
dtype: 'float32',
size: 21602,
strides: [ 1 ],
dataId: { id: 1 },
id: 1,
rankType: '2'
} Tensor {
kept: false,
isDisposedInternal: false,
shape: [ 2 ],
dtype: 'float32',
size: 2,
strides: [],
dataId: { id: 2 },
id: 2,
rankType: '1'
} 10

After long research, and a lot of time.
TensorFlow removed the .get function you would use instead arraySync.
for example.
pair.get(1)[0]
will be:
pair.arraySync(1)[0]

Related

How to include zero counts in a javascript frequency counting function for histograms

Need some help please - am using the excellent response to this histogram bucketing question as per code below. My question is what is the most elegant way to get my buckets with zero counts represented in the function output?
Current Output is:
[ [ '0', 2 ],
[ '32', 1 ],
[ '64', 2 ],
[ '80', 1 ],
[ '96', 1 ],
[ '112', 1 ] ]
which omits to note buckets 16 and 48 which have zero counts.
My desired output is:
[ [ '0', 2 ],
[ '16', 0 ],
[ '32', 1 ],
[ '48', 0 ],
[ '64', 2 ],
[ '80', 1 ],
[ '96', 1 ],
[ '112', 1 ] ]
All help much appreciated.
function defArrs (){
var arr = [1,16,38,65,78,94,105,124]
var binsize = 16;
var check = Object.entries(frequencies (arr,binsize));
console.log(check);
}
function frequencies(values, binsize) {
var mapped = values.map(function(val) {
return Math.ceil(val / binsize) -1;
});
console.log(mapped);
return mapped.reduce(function (freqs, val, i) {
var bin = (binsize * val);
freqs[bin] ? freqs[bin]++ : freqs[bin] = 1;
return freqs;
}, {});
}
Instead of starting with the empty object for .reduce:
}, {});
// ^^
construct one with all the properties you'll want to be included at the end (starting with a count of 0, of course).
const initialObj = Object.fromEntries(
Array.from(
{ length: 7 },
(_, i) => [i * binsize, 0]
)
);
And pass that as the second parameter to .reduce.
That approach also means that this
freqs[bin] ? freqs[bin]++ : freqs[bin] = 1;
will simplify to
freqs[bin]++;
Or, even better:
const frequencies = (nums, binsize) => {
const mapped = nums.map(num => Math.ceil(num / binsize) - 1;
const grouped = Object.fromEntries(
{ length: 7 },
(_, i) => [i * binsize, 0]
);
for (const val of mapped) {
grouped[binsize * val]++;
}
return grouped;
};

Convert flat array of persons into nested pedigree tree in JavaScript

I'm trying to transform a flat list of persons into a structured tree of ancestry.
The source array of persons looks like this:
const list = [
{
id: 1,
name: 'John',
akin: true,
motherId: undefined,
fatherId: undefined,
partnerIds: [2]
},
{
id: 2,
name: 'Maria',
akin: false,
motherId: undefined,
fatherId: undefined,
partnerIds: [1]
},
{
id: 3,
name: 'Steven',
akin: true,
fatherId: 1,
motherId: 2,
partnerIds: [4, 5]
},
{
id: 4,
name: 'Stella',
akin: false,
motherId: undefined,
fatherId: undefined,
partnerIds: [3]
},
{
id: 5,
name: 'Laura',
akin: false,
motherId: undefined,
fatherId: undefined,
partnerIds: [3]
},
{
id: 5,
name: 'Solomon',
akin: true,
motherId: 4,
fatherId: 3,
partnerIds: []
},
{
id: 6,
name: 'Henry',
akin: true,
fatherId: 3,
motherId: 5,
partnerIds: []
}
]
It can contain n generations of people whose direct ancestors are defined by their respective fatherId and motherId. Unknown parents (oldest known ancestor, or related only by partnership) are simply undefined.
Partnerships are indicated by an array of partnerIds.
The expected output should look like this:
const pedigree = [
{
id: 1,
name: 'John',
partnerships: [
{
partner: {
id: 2,
name: 'Maria',
},
children: [
{
id: 3,
name: 'Steven',
partnerships: [
{
partner: {
id: 4,
name: 'Stella',
},
children: [
{
id: 5,
name: 'Solomon'
}
]
},
{
partner: {
id: 5,
name: 'Laura',
},
children: [
{
id: 6,
name: 'Henry',
}
]
}
]
}
]
}
]
}
]
Visually the result would look like this:
Visual pedigree
The desired output format is not intended for storing, but for easier visualization and processing for later rendering.
I tried to loop over the flat list, create a hashTable for referencing the single persons and then find partners and common children.
My issue is though that my approach only works for two generations, or one level of nesting, although I need it to be suitable for n generations.
I think I need some recursive function or way of starting to loop up from the bottom of ancestry somehow, but I can't figure out a smart way.
I'd be glad for any suggestions or tips!
EDIT:
This is what I've tried:
const createPedigree = (dataset) => {
const hashTable = Object.create(null)
dataset.forEach(
(person) => (hashTable[person.id] = { ...person, partnerships: [] })
)
const dataTree = []
dataset.forEach((person) => {
if (person.akin) {
if (person.partnerIds.length) {
person.partnerIds.forEach((partnerId) => {
hashTable[person.id].partnerships.push({
partner: { ...dataset.find((p) => p.id === partnerId) },
children: []
})
})
}
}
dataTree.push(hashTable[person.id])
})
dataset.forEach((child) => {
// fill partnerships with children
if (child.fatherId && child.motherId) {
if (
hashTable[child.fatherId].akin &&
hashTable[child.fatherId].partnerships.length
) {
let mother = hashTable[child.fatherId].partnerships.find(
(partnership) => {
return partnership.partner.id === child.motherId
}
)
mother.children.push(child)
} else if (hashTable[child.motherId].akin) {
let father = hashTable[child.motherId].partnerships.find(
(partnership) => {
return partnership.partner.id === child.fatherId
}
)
father.children.push(child)
}
}
})
return dataTree
}
You are correct in the assumption that a general solution will involve some recursive calls (or a queue of candidates to expand until the queue is empty).
The output structure levels alternate between:
a person with partnerships
partnerships that contain a partner and children (each child is then again a 1.)
To make things simpler we can just model the 2 steps above with 2 separate functions. I chose the names expandPerson and expandPartnership.
const expandPerson = (personId, dataset) => {
// we get the person from the dataset by their id
const personData = dataset.find(p => p.id == personId)
// we clone only the data that we want in the output
const person = { id: personData.id, name: personData.name }
// all partnerIds of this person need to become their parnerships
// so we just map them to an "expanded partnership" (step 2.)
person.partnerships = personData.partnerIds
.map(partnerId => expandPartnership(partnerId, person.id, dataset))
// we return the "expanded" person
return person
}
const expandPartnership = (partner1Id, partner2Id, dataset) => {
// we get the partner from the dataset by their id
const partnerData = dataset.find(p => p.id == partner1Id)
// we clone only the data that we want in the output
const partner = { id: partnerData.id, name: partnerData.name }
// all people in the dataset, whose parents are partner1Id
// and pertner2Id are the children
const children = dataset
.filter(p => p.motherId == partner1Id && p.fatherId == partner2Id
|| p.motherId == partner2Id && p.fatherId == partner1Id)
// we map each child as an "expanded person" again (back to step 1.)
.map(p => expandPerson(p.id, dataset))
// we return the "expanded" partnership
return { partner, children }
}
In the code you then just call const pedigree = expandPerson(1, list)
If the root is not always id: 1 just find the root id first
const rootId = list.find(p => p.akin && !p.fatherId && !p.motherId).id
const pedigree = expandPerson(rootId, list)
Note: you have a duplicate id (id: 5) in the provided input. You have to fix that.

Group the parent object array based on the filter of the child object array

I've coded and I've got the results I want, but I think this code looks unclean.
const verses = [{
index: 1,
verses: [{
level: 1
},
{
level: 1
}
]
},
{
index: 2,
verses: [{
level: 1
},
{
level: 1
}
]
},
{
index: 3,
verses: [{
level: 2
}]
},
{
index: 4,
verses: [{
level: 2
}]
},
{
index: 5,
verses: [{
level: 2
},
{
level: 2
}
]
},
{
index: 6,
verses: [{
level: 3
},
{
level: 3
}
]
},
{
index: 7,
verses: [{
level: 3
},
{
level: 3
},
{
level: 4
},
{
level: 4
}
]
}
]
function getVerseIndexByLevel(level) {
const result = verses.map(v => {
const mappingByLevel = Math.max.apply(Math, [...new Set(v.verses.map(w => w.level))])
const mappingByVerse = v.index
return {
level: mappingByLevel,
verse: mappingByVerse
}
}).filter(v => v.level === level).map(v => v.verse)
return result
}
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: getVerseIndexByLevel(i)
})
}
Is my code above consuming too much performance?
Could you make it cleaner and simpler without changing the result at all?
-I don't know what details I have to add more, StackOverflow forced me to write this because the post is mostly code. but I hope you understand just by looking at the results of the code.-
On the performance end, the one thing I can see that could improve it would be to call getVerseIndexByLevel only once, and use the calculated result mapping, instead of calling it multiple times (requiring re-parsing the input every time). Something like
const verseIndicies = getVerseIndicies();
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: verseIndicies[i]
})
}
Another slight improvement - since you're using ES6 syntax, you don't need to .apply to Math.max - just spread the set into it.
You also don't need to turn the level numbers into a Set first - since it's being passed into Math.max anyway, repetitions aren't an issue.
const verses=[{index:1,verses:[{level:1},{level:1}]},{index:2,verses:[{level:1},{level:1}]},{index:3,verses:[{level:2}]},{index:4,verses:[{level:2}]},{index:5,verses:[{level:2},{level:2}]},{index:6,verses:[{level:3},{level:3}]},{index:7,verses:[{level:3},{level:3},{level:4},{level:4}]}];
function getVerseIndicies() {
const verseIndicies = {};
for (const v of verses) {
const level = Math.max(...v.verses.map(w => w.level));
verseIndicies[level] ??= [];
verseIndicies[level].push(v.index);
}
return verseIndicies;
}
const verseIndicies = getVerseIndicies();
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: verseIndicies[i]
})
}

Reorder an array of values in an object in Javascript

I have the following object that I am trying to sort so that the labels are "High, mid, low" all of the time. The order I get them are not always the same so I wanted to add another layer of ordering to ensure that I get "high, mid, low"
Before:
status:{
label:['mid', 'high', 'low'],
data:[4, 3, 1]
}
After:
status:{
label:['high', 'mid', 'low'],
data:[3, 4, 1]
}
The easiest way to sort those two arrays "linked" is by temporarily combining them into one array:
const status = {
label: ['mid', 'high', 'low'],
data: [4, 3, 1]
};
// Combine the two arrays into an array of pairs
const pairs = status.label.map((label, index) => [label, status.data[index]]);
console.log('pairsBefore', pairs); // [ ['mid', 4 ], ['high', 3 ], ['low', 1 ]]
// Used for sorting
const ORDER = ['high', 'mid', 'low'];
// Sort the pairs
pairs.sort((a, b) => {
const [labelA, dataA] = a;
const [labelB, dataB] = b;
// Gives 0 for 'high', 1 for 'mid' and 2 for 'low'
const indexA = ORDER.indexOf(labelA);
const indexB = ORDER.indexOf(labelB);
// Substract for A and B, see how Array.prototype.sort works
return indexA - indexB;
});
console.log('pairsAfter', pairs); // [ ['high', 3 ], ['mid', 4 ], ['low', 1 ]]
// Split it back into two arrays
const statusSorted = {
label: pairs.map(pair => pair[0]),
data: pairs.map(pair => pair[1]),
};
console.log('statusSorted', statusSorted);
//{
// label: ['high', 'mid', 'low'],
// data: [3, 4, 1],
//}

Iterating over a multi-level object?

I have a multi-level object that has dynamic names and goes about 2 levels deep. I came up with a solution that works if there is only one dynamic-object, however I am having trouble writing the loop for multiples. Here are the basics.
My object looks like this:
{
dynamic_name1: {
mods: [
[Object], [Object]
]
},
dynamic_name2: {
mods: [
[Object], [Object]
]
},
dynamic_name3: {
mods: [
[Object], [Object]
]
}
}
Basically, I want to iterate over this object, and get the objects from within each of the respective mods arrays, then push them to a different array.
I am getting this object, with the variable container, then structure like so:
const [objectKeys] = Object.keys(container);
const modsList = container[objectKeys].mods;
This solution works really well because then I just add it to my new array like this:
const myMods = [...modsList]
However, this whole functionality seems to break when I try to loop it. here's what I have so far:
for (j = 0; j < container.length; j++){
const [objectKeys] = Object.keys(container);
}
but when trying to log [objectKeys] I get undefined. Any idea how I could do this?
I would get all the mods reducing a map over your dynamic sub-objects, something like:
const container = {
dynamic_name1: {
mods: [
1, 2
]
},
dynamic_name2: {
mods: [
3, 4
]
},
dynamic_name3: {
mods: [
5, 6
]
}
}
const mods = Object.values(container)
.map(sub => sub.mods)
.reduce(
(result, mods) => [...result, ...mods],
[]
)
console.log(mods)
// [1, 2, 3, 4, 5, 6]
This way you can loop at the end.
If I understood you correctly, below function should accept an object and return an array with all 2nd level mods
function extractMods(container) {
return Object.keys(container).reduce((r, c) => [...r, ...container[c].mods], [])
}
Call it like below
extractMods({
a: {
mods: [0,1,2,3,4]
},
b: {
mods: [5,6,7,8,9]
},
c: {
mods: [10,11,12,13,14]
},
})
It will return
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Good example of why I dislike dynamic property names. An array consisting of: [{"name":"dynamic_name1","mods":[{},{}]},{"name":"dynamic_name2","mods":[{},{}]}] gives you easy loops.
But to help with the issue, Object.keys() has some siblings: Object.entries() and Object.values() .
const data = {
dynamic_name1: {
mods: [
{ id: 1 },
{ id: 2}
]
},
dynamic_name2: {
mods: [
{ id: 3 },
{ id: 4 }
]
},
dynamic_name3: {
mods: [
{ id: 5 },
{ id: 6 }
]
}
};
const modsList = Object.values( data ).flatMap( entry => entry.mods );
console.log( modsList );
If your browser does not support flatMap() yet, you can do the same with a map() or reduce().
let obj = {
dynamic_name1: { mods: [{x:1}, {x:2}] },
dynamic_name2: { mods: [{x:3}, {x:4}] },
dynamic_name3: { mods: [{x:5}, {x:6}] }
}
let result = Object.values(obj).reduce((a, b) => (a.push(...b.mods), a), []);
console.log( result )
The issue with
for (j = 0; j < container.length; j++){
const [objectKeys] = Object.keys(container);
}
is that container.length is undefined, therefore, there's zero iterations and objectKeys is never assigned.
Use Object.values and map the resulting array to return the mods, then flat it withdepth of 2 :
const container = {
dynamic_name1: {
mods: [
[Object],
[Object]
]
},
dynamic_name2: {
mods: [
[Object],
[Object]
]
},
dynamic_name3: {
mods: [
[Object],
[Object]
]
}
}
const myMods = Object.values(container).map(e => e.mods).flat(2);
console.log(myMods);

Categories

Resources