Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .codespellrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[codespell]
# Skip generated, vendored, binary, and lock files.
# graphx/ is ported from Apache Spark GraphX; keep it aligned with upstream.
# graphframes-connect-databricks/ holds local sbt build output (gitignored).
skip = .git,*.lock,poetry.lock,*.iml,target,build,dist,*_pb2*.py,*_pb2_grpc.py,graphx,graphframes-connect-databricks,*.svg,*.png,*.gif,*.parquet,*.csv,*.json,*.pom,*.semanticdb
# Identifiers and domain terms that are not misspellings:
# mis/MIS - Maximal Independent Set
# te/ser - local variable names in Scala tests/algorithms
# fof/mye/protocall/alledges - local variable names (camelCase) in tests
# afterall - ScalaTest `afterAll` lifecycle override
# jurney - maintainer surname (Russell Jurney)
ignore-words-list = mis,te,ser,fof,mye,protocall,alledges,afterall,jurney
6 changes: 3 additions & 3 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ jobs:
- name: Code style
working-directory: ./python
run: |
poetry run python -m black --check graphframes
poetry run python -m flake8 graphframes
poetry run python -m isort --check graphframes
poetry run python -m black --check --config pyproject.toml graphframes dev ../dev
poetry run python -m flake8 graphframes dev ../dev
poetry run python -m isort --check --settings-path pyproject.toml graphframes dev ../dev
- name: Build jar
working-directory: ./python
run: |
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/spellcheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Spell Check
on: [push, pull_request]
permissions:
contents: read
jobs:
codespell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install codespell
run: pip install codespell==2.4.2
- name: Run codespell
# Configuration (skips and ignore list) lives in .codespellrc.
run: codespell .
12 changes: 9 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ repos:
hooks:
- id: black
name: black
entry: bash -c 'cd python && poetry run black graphframes/' --
entry: bash -c 'cd python && poetry run black --config pyproject.toml graphframes/ dev/ ../dev/' --
language: system
types: [python]

- id: flake8
name: flake8
entry: bash -c 'cd python && poetry run flake8 graphframes/' --
entry: bash -c 'cd python && poetry run flake8 graphframes/ dev/ ../dev/' --
language: system
types: [python]

- id: isort
name: isort
entry: bash -c 'cd python && poetry run isort graphframes/' --
entry: bash -c 'cd python && poetry run isort --settings-path pyproject.toml graphframes/ dev/ ../dev/' --
language: system
types: [python]

Expand All @@ -32,3 +32,9 @@ repos:
language: system
types: [scala]
pass_filenames: false

- repo: https://github.com/codespell-project/codespell
rev: v2.4.2
hooks:
- id: codespell
# Configuration (skips and ignore list) lives in .codespellrc.
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/graphf
We are using the following tools for enforcing of the codestyle:

- for python we are relying on the `black` + `flake8` + `isort`;
- for scala we are relying on the `scalafmt`.
- for scala we are relying on the `scalafmt`;
- for spelling (across yml, python, and scala) we are relying on `codespell` (configured in `.codespellrc`).

You can enforce the code style using the [pre-commit-hooks](https://pre-commit.com/):

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ There are some popular use cases when GraphFrames is almost irreplaceable, inclu
- Compliance analytics with a scalable shortest paths algorithm and motif analysis;
- Anti-fraud with scalable cycles detection in large networks and by using K-Core algorithm;
- Identity resolution at the scale of billions with highly efficient connected components;
- Plan marketing campaigns in social networks using Maximal Indpendent Set algorithm;
- Plan marketing campaigns in social networks using Maximal Independent Set algorithm;
- Rank search result with a distributed, Pregel-based PageRank;
- Cluster huge graphs with Label Propagation and Power Iteration Clustering;
- Compute node embeddings at billion scale using Random-Walks and Hash2Vec model;
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/org/graphframes/GraphFrame.scala
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ class GraphFrame private (
case VarLengthPattern(src, name, min, max, direction, dst) =>
if (min.isEmpty || max.isEmpty) {
throw new InvalidParseException(
s"Unbounded length patten ${pattern} is not supported! " +
s"Unbounded length pattern ${pattern} is not supported! " +
"Please a pattern of defined length.")
}
findVarLengthPattern(src, name, min.toInt, max.toInt, direction, dst)
Expand Down Expand Up @@ -982,7 +982,7 @@ class GraphFrame private (
* large-scale sparse graphs." Proceedings of Simpósio Brasileiro de Pesquisa Operacional
* (SBPO’15) (2015): 1-11.
*
* Returns a DataFrame with unque cycles.
* Returns a DataFrame with unique cycles.
*
* @return
* an instance of DetectingCycles initialized with the current context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class RandomWalkEmbeddings private[graphframes] (private val graph: GraphFrame)
.run()
} else {
// we need to create a new DataFrame, so unpersisting peristedEmbeddings
// does not accidently unpersist the result;
// does not accidentally unpersist the result;
// dummy operations are cheap, but will create a different plan.
persistedEmbeddings
.withColumnRenamed(RandomWalkEmbeddings.embeddingColName, "x")
Expand Down Expand Up @@ -269,7 +269,7 @@ object RandomWalkEmbeddings extends Serializable {
* provide a smooth way to initialize the whole embeddings pipeline with a single method call
* that is usable for Python API (py4j and Spark Connect).
*
* Instead of this API it is recommended to use new + settters of the class!
* Instead of this API it is recommended to use new + setters of the class!
*/
def pythonAPI(
graph: GraphFrame,
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/org/graphframes/lib/AllPaths.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import org.graphframes.WithLocalCheckpoints
* - `len`: number of edges in the path (Long)
*
* Note: in the case of undirected graph an algorithm run on the internal graph made by union
* edges and reversed edges. It is assummed that graph does not have multi-edges. Results may be
* edges and reversed edges. It is assumed that graph does not have multi-edges. Results may be
* unstable and unpredictable for the graph with multi-edges.
*/
class AllPaths private[graphframes] (private val graph: GraphFrame)
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/org/graphframes/lib/HyperANF.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class HyperANF private[graphframes] (graph: GraphFrame)
.groupBy(col(GraphFrame.SRC).alias(GraphFrame.ID))
.agg(hll_union_agg(s"hop_${hop - 1}").alias(s"hop_${hop}"))

// stanard GF persist-unpersist-checkpoint flow
// standard GF persist-unpersist-checkpoint flow
state = {
val stateToPersist = state.join(nState, GraphFrame.ID)
if (shouldCheckpoint && hop % checkpointInterval == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private object LabelPropagation {
// Overall:
// - Initial labels - IDs
// - Active vertex col (halt voting) - did the label changed?
// - Choosing a new label - top across neighbours (tie-braking is determenistic)
// - Choosing a new label - top across neighbours (tie-braking is deterministic)

val preparedGraph = GraphFrame(
graph.vertices.select(GraphFrame.ID),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ object MaximalIndependentSet extends Serializable with Logging {
val joinedMIS =
isJoinedMIS.filter((!col(notJoinedMISCol)) && col(isNominated)).select(GraphFrame.ID)

// update curent MIS
// update current MIS
val updatedMIS = misDF
.join(
isolatedVertices.select(col(GraphFrame.ID), lit(true).alias("f")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private[graphframes] object RandomizedContraction extends Logging with Serializa
}

logInfo(s"graph was successfully contracted for $iter iterations")
logInfo("start reverse tranformation")
logInfo("start reverse transformation")

var accA = 1L
var accB = 0L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private object ShortestPaths extends Logging {

// For landmark vertices the initial distance to itself is set to 0
// Example: graph with vertices a, b, c, d; landmarks = (c, d)
// we shoudl init the following:
// we should init the following:
// (a, Map()), (b, Map()), (c, Map(c -> 0)), (d, Map(d -> 0))
//
// Inside the following function it is done by applying multiple case-when
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/org/graphframes/mixins.scala
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ private[graphframes] trait WithDirection {
}

/**
* Gets should graph be considred as directed.
* Gets should graph be considered as directed.
*
* @return
* true if directed
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/org/graphframes/pattern/patterns.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private[graphframes] object Pattern {
}

/**
* Rewirte a motif string if there are incomming edges
* Rewrite a motif string if there are incoming edges
*/
private[graphframes] def rewriteIncomingEdges(patterns: String): String = {
val reversedEdge =
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/org/graphframes/rw/RandomWalkBase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ trait RandomWalkBase extends Serializable with Logging with WithIntermediateStor
/** Unique identifier for the current random walk run. */
protected var runID: String = java.util.UUID.randomUUID().toString

/** Starting batch index for continous mode */
/** Starting batch index for continuous mode */
protected var startingIteration: Int = 1

/** Internal handler of vertex data type */
Expand Down Expand Up @@ -187,7 +187,7 @@ trait RandomWalkBase extends Serializable with Logging with WithIntermediateStor
def getRunId(): String = runID

/**
* Sets the startng batch index for the continous mode. See @setWalkId comment for details.
* Sets the startng batch index for the continuous mode. See @setWalkId comment for details.
*
* @param value
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Hash2VecSuite extends SparkFunSuite with GraphFrameTestSparkContext with B
assert(collected.length === uniqueElementsCnt)
}

test("hash2vec reproducable with seed") {
test("hash2vec reproducible with seed") {
val hash2vecResults =
new Hash2Vec().setSequenceCol("seq").setHashingSeed(42).run(longSequences)
val hash2vecResults2 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class AggregateMessagesSuite extends SparkFunSuite with GraphFrameTestSparkConte
aggMap.keys.foreach { case user =>
assert(aggMap(user) === trueAgg(user), s"Failure on user $user")
}
// Perfom the aggregation again, this time providing the messages as Strings instead.
// Perform the aggregation again, this time providing the messages as Strings instead.
val msgToSrc2 = "(dst['age'] + CASE WHEN (edge['relationship'] = 'friend') THEN 1 ELSE 0 END)"
val msgToDst2 = "src['age']"
val agg2 = g.aggregateMessages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class PatternSuite extends SparkFunSuite {
UndirectedEdge(AnonymousEdge(NamedVertex("v"), NamedVertex("k")))))
}

test("rewrite incomming edges") {
test("rewrite incoming edges") {
assert(Pattern.rewriteIncomingEdges("(u)<-[e]-(v);") === "(v)-[e]->(u)")
assert(Pattern.rewriteIncomingEdges("!(u)<-[e]-(v);") === "!(v)-[e]->(u)")
assert(
Expand All @@ -130,7 +130,7 @@ class PatternSuite extends SparkFunSuite {
"(v1)<-[e*1..2]->(v2)") === "(v1)-[e*1..2]->(v2);(v2)-[e*1..2]->(v1)")
}

test("rewrite incomming edges and parse") {
test("rewrite incoming edges and parse") {
Pattern.parse("(v)<-[e]-(u)") === Pattern.parse("(u)-[e]->(v)")
Pattern.parse("(v)<-[]-(u)") === Pattern.parse("(u)-[]->(v)")
Pattern.parse("!(v)<-[]-(u)") === Pattern.parse("!(u)-[]->(v)")
Expand Down
4 changes: 2 additions & 2 deletions docs/mdoc/01-installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ If you are new to using Apache Spark, refer to the [Apache Spark Documentation](

## Maven Central Coordinates

GraphFrames core is [published](https://central.sonatype.com/namespace/io.graphframes) in the Maven Central under namespace `io.graphframes`. All the artifacts are groupped using the following logic.
GraphFrames core is [published](https://central.sonatype.com/namespace/io.graphframes) in the Maven Central under namespace `io.graphframes`. All the artifacts are grouped using the following logic.

```
graphframes-{component-name}-{spark-major-version}_{scala-version}
Expand Down Expand Up @@ -144,7 +144,7 @@ The only runtime dependency of the GraphFrames core is `graphframes-graphx-*`. I

### Microsoft Fabric Note

It was [reported](https://github.com/graphframes/graphframes/issues/748) that Microsft Fabric cannot resolve runtime dependencies of JVM pckages automatically. MS Fabric users should specify both core and `graphframes-graphx-*` (depends on the Apache Spark version).
It was [reported](https://github.com/graphframes/graphframes/issues/748) that Microsoft Fabric cannot resolve runtime dependencies of JVM packages automatically. MS Fabric users should specify both core and `graphframes-graphx-*` (depends on the Apache Spark version).

## Building GraphFrames from Source

Expand Down
2 changes: 1 addition & 1 deletion docs/src/02-quick-start/02-quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ supported algorithms:
| Triangle Count | Yes | Yes | GF provides smoother API |
| SVD++ | Yes | No | GX |
| Cycles Detection | No | Yes | GF |
| Triangel Count | No | Yes | GF |
| Triangle Count | No | Yes | GF |
| K-Core | No | Yes | GF |
| Maximal Independent Set | No | Yes | GF |
| Approximate Neighbor Functions | No | Yes | GF |
Expand Down
6 changes: 3 additions & 3 deletions docs/src/03-tutorials/02-motif-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ Edge count: 97,104 == Valid edge count: 97,104

Let's look for a simple motif: a directed triangle. We will find all instances of a directed triangle in the graph. The @pydoc(graphframes.GraphFrame.find) method takes a string as an argument that specifies the structure of a motif one edge at a time, in the same syntax as Cypher, with a semi-colon between edges. For a triangle motif, that works out to: `(a)-[e]->(b); (b)-[e2]->(c); (c)-[e3]->(a)`. Edge labels are optional, this is valid graph query: `(a)-[]->(b)`.

The `g.find()` method returns a `DataFrame` with fields fo each of the node and edge labels in the pattern. To further express the motif you're interested in, you can now use relational `DataFrame` operations to filter, group, and aggregate the results. This makes the network motif finding in GraphFrames very powerful, and this type of property graph motif was originally defined in the [graphframes paper](https://people.eecs.berkeley.edu/~matei/papers/2016/grades_graphframes.pdf).
The `g.find()` method returns a `DataFrame` with fields for each of the node and edge labels in the pattern. To further express the motif you're interested in, you can now use relational `DataFrame` operations to filter, group, and aggregate the results. This makes the network motif finding in GraphFrames very powerful, and this type of property graph motif was originally defined in the [graphframes paper](https://people.eecs.berkeley.edu/~matei/papers/2016/grades_graphframes.pdf).

A complete description of the graph query language is in the [GraphFrames User Guide](/04-user-guide/04-motif-finding.md). Let's look at an example: a directed triangle. We will find all instances of a directed triangle in the graph.

Expand Down Expand Up @@ -428,9 +428,9 @@ The result is a count of the divergent triangles in the graph by type.

## Property Graph Motifs

Simple motif finding can be used to explore a knowledge graph. It is also possibel to use domain knowledge to define and match known patterns and then explore new variant motifs. This can be used to apply and then expand domain knowledge about a knowledge graph. It is powerful stuff!
Simple motif finding can be used to explore a knowledge graph. It is also possible to use domain knowledge to define and match known patterns and then explore new variant motifs. This can be used to apply and then expand domain knowledge about a knowledge graph. It is powerful stuff!

We can do more with the properties of paths than just count them by node and edge type. We can use the properties of the nodes and edges in the paths to filter, group, and aggregate the results to form *property graph motifs*. Such complex motifs were first defined (without being formally named) in the paper describing this prject: [GraphFrames: An Integrated API for Mixing Graph and Relational Queries, Dave et al. 2016](https://people.eecs.berkeley.edu/~matei/papers/2016/grades_graphframes.pdf). They are a combination of graph and relational queries. We can use them to find complex patterns in the graph.
We can do more with the properties of paths than just count them by node and edge type. We can use the properties of the nodes and edges in the paths to filter, group, and aggregate the results to form *property graph motifs*. Such complex motifs were first defined (without being formally named) in the paper describing this project: [GraphFrames: An Integrated API for Mixing Graph and Relational Queries, Dave et al. 2016](https://people.eecs.berkeley.edu/~matei/papers/2016/grades_graphframes.pdf). They are a combination of graph and relational queries. We can use them to find complex patterns in the graph.

The larger motifs get, the more interesting they are. Five nodes is often the limit with a Spark cluster, depending on how large your graph is. In this instance I will limit myself to a 4-path pattern as you may not have a Spark cluster on which to learn. Keep in mind that I am talking about paths - through aggregation a motif might cover thousands of nodes!

Expand Down
4 changes: 2 additions & 2 deletions docs/src/04-user-guide/03-centralities.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ For `graphframes` only. To avoid exponential growing of the Spark' Logical Plan,

- `use_local_checkpoints`

For `graphframes` only. By default, GraphFrames uses persistent checkpoints. They are realiable and reduce the errors rate. The downside of the persistent checkpoints is that they are requiride to set up a `checkpointDir` in persistent storage like `S3` or `HDFS`. By providing `use_local_checkpoints=True`, user can say GraphFrames to use local disks of Spark' executurs for checkpointing. Local checkpoints are faster, but they are less reliable: if the executur lost, for example, is taking by the higher priority job, checkpoints will be lost and the whole job fails.
For `graphframes` only. By default, GraphFrames uses persistent checkpoints. They are reliable and reduce the errors rate. The downside of the persistent checkpoints is that they are requiride to set up a `checkpointDir` in persistent storage like `S3` or `HDFS`. By providing `use_local_checkpoints=True`, user can say GraphFrames to use local disks of Spark' executurs for checkpointing. Local checkpoints are faster, but they are less reliable: if the executur lost, for example, is taking by the higher priority job, checkpoints will be lost and the whole job fails.

- `storage_level`

The level of storage for intermediate results and the output `DataFrame` with components. By default it is memory and disk deserialized as a good balance between performance and reliability. For very big graphs and out-of-core scenarious, using `DISK_ONLY` may be faster.
The level of storage for intermediate results and the output `DataFrame` with components. By default it is memory and disk deserialized as a good balance between performance and reliability. For very big graphs and out-of-core scenarios, using `DISK_ONLY` may be faster.

### Python API

Expand Down
Loading
Loading