diff --git a/doc/asciidoc/scripts/similarity-cosine.cypher b/doc/asciidoc/scripts/similarity-cosine.cypher index 77756eae5..f3031c2c9 100644 --- a/doc/asciidoc/scripts/similarity-cosine.cypher +++ b/doc/asciidoc/scripts/similarity-cosine.cypher @@ -43,6 +43,23 @@ MERGE (karin)-[:LIKES {score: 10}]->(portuguese) // end::create-sample-graph[] +// tag::function-cypher[] +MATCH (p1:Person {name: 'Michael'})-[likes1:LIKES]->(cuisine) +MATCH (p2:Person {name: "Arya"})-[likes2:LIKES]->(cuisine) +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.cosine(collect(likes1.score), collect(likes2.score)) AS similarity +// end::function-cypher[] + +// tag::function-cypher-all[] +MATCH (p1:Person {name: 'Michael'})-[likes1:LIKES]->(cuisine) +MATCH (p2:Person)-[likes2:LIKES]->(cuisine) WHERE p2 <> p1 +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.cosine(collect(likes1.score), collect(likes2.score)) AS similarity +ORDER BY similarity DESC +// end::function-cypher-all[] + // tag::stream[] MATCH (p:Person), (c:Cuisine) OPTIONAL MATCH (p)-[likes:LIKES]->(c) diff --git a/doc/asciidoc/scripts/similarity-euclidean.cypher b/doc/asciidoc/scripts/similarity-euclidean.cypher index ac5eddf1d..60679d96e 100644 --- a/doc/asciidoc/scripts/similarity-euclidean.cypher +++ b/doc/asciidoc/scripts/similarity-euclidean.cypher @@ -9,6 +9,8 @@ MERGE (italian:Cuisine {name:'Italian'}) MERGE (indian:Cuisine {name:'Indian'}) MERGE (lebanese:Cuisine {name:'Lebanese'}) MERGE (portuguese:Cuisine {name:'Portuguese'}) +MERGE (british:Cuisine {name:'British'}) +MERGE (mauritian:Cuisine {name:'Mauritian'}) MERGE (zhen:Person {name: "Zhen"}) MERGE (praveena:Person {name: "Praveena"}) @@ -18,23 +20,46 @@ MERGE (karin:Person {name: "Karin"}) MERGE (praveena)-[:LIKES {score: 9}]->(indian) MERGE (praveena)-[:LIKES {score: 7}]->(portuguese) +MERGE (praveena)-[:LIKES {score: 8}]->(british) +MERGE (praveena)-[:LIKES {score: 1}]->(mauritian) MERGE (zhen)-[:LIKES {score: 10}]->(french) MERGE (zhen)-[:LIKES {score: 6}]->(indian) +MERGE (zhen)-[:LIKES {score: 2}]->(british) MERGE (michael)-[:LIKES {score: 8}]->(french) MERGE (michael)-[:LIKES {score: 7}]->(italian) MERGE (michael)-[:LIKES {score: 9}]->(indian) +MERGE (michael)-[:LIKES {score: 3}]->(portuguese) MERGE (arya)-[:LIKES {score: 10}]->(lebanese) MERGE (arya)-[:LIKES {score: 10}]->(italian) MERGE (arya)-[:LIKES {score: 7}]->(portuguese) +MERGE (arya)-[:LIKES {score: 9}]->(mauritian) MERGE (karin)-[:LIKES {score: 9}]->(lebanese) MERGE (karin)-[:LIKES {score: 7}]->(italian) +MERGE (karin)-[:LIKES {score: 10}]->(portuguese) // end::create-sample-graph[] +// tag::function-cypher[] +MATCH (p1:Person {name: 'Zhen'})-[likes1:LIKES]->(cuisine) +MATCH (p2:Person {name: "Praveena"})-[likes2:LIKES]->(cuisine) +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.euclideanDistance(collect(likes1.score), collect(likes2.score)) AS similarity +// end::function-cypher[] + +// tag::function-cypher-all[] +MATCH (p1:Person {name: 'Zhen'})-[likes1:LIKES]->(cuisine) +MATCH (p2:Person)-[likes2:LIKES]->(cuisine) WHERE p2 <> p1 +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.euclideanDistance(collect(likes1.score), collect(likes2.score)) AS similarity +ORDER BY similarity DESC +// end::function-cypher-all[] + // tag::stream[] MATCH (p:Person), (c:Cuisine) OPTIONAL MATCH (p)-[likes:LIKES]->(c) diff --git a/doc/asciidoc/scripts/similarity-jaccard.cypher b/doc/asciidoc/scripts/similarity-jaccard.cypher index 49ef044f6..54ff657fe 100644 --- a/doc/asciidoc/scripts/similarity-jaccard.cypher +++ b/doc/asciidoc/scripts/similarity-jaccard.cypher @@ -35,6 +35,28 @@ MERGE (karin)-[:LIKES]->(italian) // end::create-sample-graph[] + +// tag::function-cypher[] +MATCH (p1:Person {name: 'Karin'})-[:LIKES]->(cuisine1) +WITH p1, collect(id(cuisine1)) AS p1Cuisine +MATCH (p2:Person {name: "Arya"})-[:LIKES]->(cuisine2) +WITH p1, p1Cuisine, p2, collect(id(cuisine2)) AS p2Cuisine +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.jaccard(p1Cuisine, p2Cuisine) AS similarity +// end::function-cypher[] + +// tag::function-cypher-all[] +MATCH (p1:Person {name: 'Karin'})-[:LIKES]->(cuisine1) +WITH p1, collect(id(cuisine1)) AS p1Cuisine +MATCH (p2:Person)-[:LIKES]->(cuisine2) WHERE p1 <> p2 +WITH p1, p1Cuisine, p2, collect(id(cuisine2)) AS p2Cuisine +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.jaccard(p1Cuisine, p2Cuisine) AS similarity +ORDER BY similarity DESC +// end::function-cypher-all[] + // tag::stream[] MATCH (p:Person)-[:LIKES]->(cuisine) WITH {item:id(p), categories: collect(id(cuisine))} as userData diff --git a/doc/asciidoc/scripts/similarity-pearson.cypher b/doc/asciidoc/scripts/similarity-pearson.cypher index d13040376..4dac86133 100644 --- a/doc/asciidoc/scripts/similarity-pearson.cypher +++ b/doc/asciidoc/scripts/similarity-pearson.cypher @@ -45,6 +45,28 @@ MERGE (karin)-[:RATED {score: 9}]->(gruffalo) // end::create-sample-graph[] +// tag::function-cypher[] +MATCH (p1:Person {name: 'Arya'})-[rated:RATED]->(movie) +WITH p1, algo.similarity.asVector(movie, rated.score) AS p1Vector +MATCH (p2:Person {name: 'Karin'})-[rated:RATED]->(movie) +WITH p1, p2, p1Vector, algo.similarity.asVector(movie, rated.score) AS p2Vector +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.pearson(p1Vector, p2Vector, {vectorType: "maps"}) AS similarity +// end::function-cypher[] + +// tag::function-cypher-all[] +MATCH (p1:Person {name: 'Arya'})-[rated:RATED]->(movie) +WITH p1, algo.similarity.asVector(movie, rated.score) AS p1Vector +MATCH (p2:Person)-[rated:RATED]->(movie) WHERE p2 <> p1 +WITH p1, p2, p1Vector, algo.similarity.asVector(movie, rated.score) AS p2Vector +RETURN p1.name AS from, + p2.name AS to, + algo.similarity.pearson(p1Vector, p2Vector, {vectorType: "maps"}) AS similarity +ORDER BY similarity DESC +// end::function-cypher-all[] + + // tag::stream[] MATCH (p:Person), (m:Movie) OPTIONAL MATCH (p)-[rated:RATED]->(m) diff --git a/doc/asciidoc/similarity-cosine.adoc b/doc/asciidoc/similarity-cosine.adoc index b26417464..cae784c0d 100644 --- a/doc/asciidoc/similarity-cosine.adoc +++ b/doc/asciidoc/similarity-cosine.adoc @@ -79,6 +79,41 @@ image::cosine-similarity2.png[role="middle"] include::scripts/similarity-cosine.cypher[tag=create-sample-graph] ---- +.The following will return the Cosine similarity of Michael and Arya: +[source, cypher] +---- +include::scripts/similarity-cosine.cypher[tag=function-cypher] +---- + +// tag::function-cypher[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Michael" | "Arya" | 0.9788908326303921 + +|=== +// end::function-cypher[] + +.The following will return the Cosine similarity of Michael and the other people that have a cuisine in common: +[source, cypher] +---- +include::scripts/similarity-cosine.cypher[tag=function-cypher-all] +---- + +// tag::function-cypher--all[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Michael" | "Arya" | 0.9788908326303921 +| "Michael" | "Zhen" | 0.9542262139256075 +| "Michael" | "Praveena" | 0.9429903335828894 +| "Michael" | "Karin" | 0.8498063272285821 + +|=== +// end::function-cypher-all[] + .The following will return a stream of node pairs along with their Cosine similarities: [source, cypher] ---- diff --git a/doc/asciidoc/similarity-euclidean.adoc b/doc/asciidoc/similarity-euclidean.adoc index 29417e7d0..49fee6a23 100644 --- a/doc/asciidoc/similarity-euclidean.adoc +++ b/doc/asciidoc/similarity-euclidean.adoc @@ -73,6 +73,39 @@ These two lists of numbers have a euclidean distance of 8.42. include::scripts/similarity-euclidean.cypher[tag=create-sample-graph] ---- +.The following will return the Euclidean distance of Zhen and Praveena: +[source, cypher] +---- +include::scripts/similarity-euclidean.cypher[tag=function-cypher] +---- + +// tag::function-cypher[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Zhen" | "Praveena" | 6.708203932499369 + +|=== +// end::function-cypher[] + +.The following will return the Euclidean distance of Zhen and the other people that have a cuisine in common: +[source, cypher] +---- +include::scripts/similarity-euclidean.cypher[tag=function-cypher-all] +---- + +// tag::function-cypher--all[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Zhen" | "Praveena" | 6.708203932499369 +| "Zhen" | "Michael" | 3.605551275463989 + +|=== +// end::function-cypher-all[] + .The following will return a stream of node pairs, along with their intersection and euclidean similarities: [source, cypher] ---- diff --git a/doc/asciidoc/similarity-jaccard.adoc b/doc/asciidoc/similarity-jaccard.adoc index 271349644..f022cc0dd 100644 --- a/doc/asciidoc/similarity-jaccard.adoc +++ b/doc/asciidoc/similarity-jaccard.adoc @@ -79,6 +79,41 @@ J(A,B) = 2 / 3 + 4 - 2 include::scripts/similarity-jaccard.cypher[tag=create-sample-graph] ---- +.The following will return the Jaccard similarity of Karin and Arya: +[source, cypher] +---- +include::scripts/similarity-jaccard.cypher[tag=function-cypher] +---- + +// tag::function-cypher[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Karin" | "Arya" | 0.66 + +|=== +// end::function-cypher[] + +.The following will return the Jaccard similarity of Karin and the other people that have a cuisine in common: +[source, cypher] +---- +include::scripts/similarity-jaccard.cypher[tag=function-cypher-all] +---- + +// tag::function-cypher--all[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Karin" | "Arya" | 0.66 +| "Karin" | "Michael" | 0.25 +| "Karin" | "Praveena" | 0.0 +| "Karin" | "Zhen" | 0.0 + +|=== +// end::function-cypher-all[] + .The following will return a stream of node pairs along with their intersection and Jaccard similarities: [source, cypher] ---- diff --git a/doc/asciidoc/similarity-pearson.adoc b/doc/asciidoc/similarity-pearson.adoc index 67ff5ab1e..c13b06bc3 100644 --- a/doc/asciidoc/similarity-pearson.adoc +++ b/doc/asciidoc/similarity-pearson.adoc @@ -66,6 +66,47 @@ include::scripts/similarity-pearson.cypher[tag=function] include::scripts/similarity-pearson.cypher[tag=create-sample-graph] ---- +.The following will return the Pearson similarity of Arya and Karin: +[source, cypher] +---- +include::scripts/similarity-pearson.cypher[tag=function-cypher] +---- + +// tag::function-cypher[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +|"Arya" | "Karin" | 0.8194651785206903 + +|=== +// end::function-cypher[] + + +In this example, we pass in `vectorType: "maps"` as an extra parameter, as well as using the `algo.similarity.asVector` function to construct a vector of maps containing each movie and the corresponding rating. +We do this because the Pearson Similarity algorithm needs to compute the average of *all* the movies that a user has reviewed, not just the ones that they have in common with the user we're comparing them to. +We can't therefore just pass in collections of the ratings of movies that have been reviewed by both people. + +.The following will return the Pearson similarity of Arya and other people that have rated at least one movie: +[source, cypher] +---- +include::scripts/similarity-pearson.cypher[tag=function-cypher-all] +---- + +// tag::function-cypher--all[] +.Results +[opts="header"] +|=== +| `from` | `to` | `similarity` +| "Arya" | "Karin" | 0.8194651785206903 +| "Arya" | "Zhen" | 0.4839533792540704 +| "Arya" | "Praveena" | 0.09262336892949784 +| "Arya" | "Michael" | -0.9551953674747637 + +|=== +// end::function-cypher-all[] + + .The following will return a stream of node pairs along with their Pearson similarities: [source, cypher] ----