patterns.js generates various pattern analysis results. Currently matches Billa products with Spar products and spits out some files to be imported as carts.

This commit is contained in:
Mario Zechner 2023-06-25 23:52:39 +02:00
parent cb38e6c5c1
commit d09bdc47e6
5 changed files with 88 additions and 8 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ tmp-dir/
site/data/latest-canonical*.json*
site/output
copy
patterns

62
patterns.js Normal file
View File

@ -0,0 +1,62 @@
const fs = require("fs");
const analysis = require("./analysis");
const knn = require("./site/js/knn");
if (!fs.existsSync("patterns")) fs.mkdirSync("patterns");
if (!fs.existsSync("patterns/sorted-billa-spar.json")) {
const items = analysis.readJSON("data/latest-canonical.json.br");
const billaSparItem = items
.filter((item) => item.store == "billa" || item.store == "spar")
.filter((item) => {
return !(
item.name.includes("Clever") ||
item.name.includes("S-BUDGET") ||
item.name.includes("Ja! Natürlich") ||
item.name.includes("SPAR") ||
item.name.includes("BILLA")
);
});
knn.vectorizeItems(billaSparItem);
const billaItems = billaSparItem.filter((item) => item.store == "billa");
const sparItems = billaSparItem.filter((item) => item.store == "spar");
console.log(billaItems.length + " " + sparItems.length);
const sortedItems = [];
sparItems.forEach((item) => (item.sorted = false));
const total = billaItems.length;
while (billaItems.length > 0) {
const refItem = billaItems.shift();
const similar = knn.findMostSimilarItem(refItem, sparItems);
if (similar.item != null) {
sortedItems.push(refItem);
sortedItems.push(similar.item);
} else {
console.log("No similar item found for " + refItem.name);
}
if (sortedItems.length % 100 == 0) console.log(sortedItems.length / 2 + "/" + total);
}
analysis.writeJSON("patterns/sorted-billa-spar.json", sortedItems);
}
const sortedItems = analysis.readJSON("patterns/sorted-billa-spar.json");
const filteredItems = [];
let result = "";
for (let i = 0; i < sortedItems.length; i += 2) {
const a = sortedItems[i];
const b = sortedItems[i + 1];
const similarity = knn.dotProduct(a.vector, b.vector);
result += "billa " + a.name + "\n";
result += "spar " + b.name + " " + similarity.toFixed(5) + "\n\n";
if (b.priceHistory.some((price) => price.price == a.price) && a.quantity == b.quantity) {
filteredItems.push(a);
filteredItems.push(b);
}
}
console.log("Sorted: " + sortedItems.length);
analysis.writeJSON("patterns/sorted-billa-spar-cart.json", { name: "Billa Spar Sortiert", items: sortedItems });
console.log("Filtered: " + filteredItems.length);
analysis.writeJSON("patterns/sorted-billa-spar-filtered-cart.json", { name: "Billa Spar Sortiert Gefiltert", items: filteredItems });
fs.writeFileSync("patterns/sorted-billa-spar.txt", result, "utf-8");

View File

@ -133,7 +133,7 @@ function loadCart() {
});
cartList.elements.numItemsLabel.innerHTML = "<strong>Artikel:</strong>";
cartList.elements.enableChart.checked = true;
cartList.elements.chart.elements.sumStores.checked = true;
cartList.elements.chart.elements.sumStores.checked = models.items.length < 2000;
if (cart.items.length == 0) {
elements.noItems.classList.remove("hidden");

View File

@ -46,14 +46,19 @@ function findMostSimilarItem(refItem, items) {
let maxSimilarity = -1;
let similarItem = null;
let similarItemIdx = -1;
items.forEach((item, idx) => {
for (let idx = 0; idx < items.length; idx++) {
const item = items[idx];
if (item.sorted) continue;
let similarity = dotProduct(refItem.vector, item.vector);
if (similarity > maxSimilarity) {
if (similarity > maxSimilarity || similarity > 0.9999999) {
maxSimilarity = similarity;
similarItem = item;
similarItemIdx = idx;
}
});
if (similarity > 0.9999999) {
break;
}
}
return {
similarity: maxSimilarity,
item: similarItem,
@ -96,16 +101,19 @@ function findMostSimilarItems(refItem, items, k = 5, accept = (ref, item) => tru
}
exports.findMostSimilarItems = findMostSimilarItems;
function similaritySortItems(items) {
function similaritySortItems(items, progress) {
if (items.length == 0) return items;
sortedItems = [items.shift()];
let refItem = sortedItems[0];
while (items.length > 0) {
items.forEach((item) => (item.sorted = false));
while (items.length != sortedItems.length) {
const similarItem = findMostSimilarItem(refItem, items);
sortedItems.push(similarItem.item);
items.splice(similarItem.index, 1);
similarItem.item.sorted = true;
refItem = similarItem.item;
if (progress) progress(sortedItems, items);
}
items.forEach((item) => delete item.sorted);
return sortedItems;
}
exports.similaritySortItems = similaritySortItems;

View File

@ -36,7 +36,16 @@ class Carts extends Model {
}
save() {
localStorage.setItem("carts", JSON.stringify(this._carts, null, 2));
const carts = [];
for (const cart of this._carts) {
carts.push({
name: cart.name,
items: cart.items.map((item) => {
return { store: item.store, id: item.id };
}),
});
}
localStorage.setItem("carts", JSON.stringify(carts, null, 2));
this.notify();
}