Step - sample
sample(n) keeps a random sample of n traversers of the whole stream, drawn without replacement
(TinkerPop SampleGlobalStep). sample(Scope.local, n) samples n elements of the list (or
entries of the map) each traverser holds (SampleLocalStep).
g.with("random.seed", 42).V().values("name").sample(2).to_list() // ["vadas", "josh"]
g.with("random.seed", 42).V().values("name").fold().sample(Scope.local, 3).to_list()
// [["josh", "peter", "marko"]]
g.V("1").values("age").sample(Scope.local, 5).to_list() // [29]
- A stream (or a list) of at most
nelements passes unchanged. sample(Scope.local, n)keeps the drawn elements in the order they were drawn; a value that is not a list or map (a number, a vertex, a path,null) passes through.- A negative
nsamples nothing. - Merged traversers are sampled unit by unit, so the sample never stands for more than
ntraversers. - In a
repeat()body,sample(n)samples each iteration's frontier (g.V().repeat(__.sample(2)).times(2)yields 2 vertices); inlocal()it samples per traverser (g.V().local(__.outE().sample(1))).
Weights
A by() after sample(n) weighs each traverser: it is drawn with a probability proportional to
its weight.
g.E().sample(2).by("weight") // heavier edges are more likely
g.V().sample(1).by(__.outE().count()) // vertices with more out-edges are more likely
- The weight must be a number of at least 0; anything else fails with
ValueError::SampleWeight, whose help shows how to convert it. - A traverser whose
by()is unproductive (a missing property) is dropped, as inorder(). - A weight of 0 is never drawn, so fewer than
ntraversers come back when fewer have a positive weight. TinkerPop's sampling loop does not end in that case; this is a deliberate difference. - Only one
by()is allowed; a second one fails withInvalidModulator.
Reproducible samples
sample(), coin() and order().by(Order.shuffle) draw from one random number generator per
execution, shared by child traversals. Without options it is seeded from the operating system's
entropy (also in the browser playground). With g.with("random.seed", n) (any integer) the same
query draws the same values on every run with the same Graphersal version:
g.with("random.seed", 42).V().values("name").order().by(Order.shuffle).to_list()
// ["marko", "ripple", "peter", "lop", "vadas", "josh"]
The option is Graphersal's counterpart of TinkerPop's SeedStrategy. It does not reproduce the
draws of TinkerPop's Java Random for the same seed, and the SeedStrategy scenarios of the
TinkerPop suite stay out of scope with withStrategies(). See the
execution options reference.
Order.shuffle
order().by(Order.shuffle) puts the stream (or, with order(Scope.local), the list) into random
order. As in TinkerPop, a clause after the shuffle still sorts: the items are shuffled first and
then stably sorted by the clauses that follow the last Order.shuffle, so
order().by(Order.shuffle).by("age") is sorted by age with ties in random order.
Rust: GraphTraversalSource::sample(n), sample_scoped(Scope, n), Order::Shuffle;
.with("random.seed", n) on the source.