pyroid.datasketch¶
An exact drop-in for datasketch 2.0.0's MinHash family: MinHash, LeanMinHash, MinHashLSH,
MinHashLSHForest and MinHashLSHEnsemble. Guide: pyroid.datasketch.
pyroid.datasketch¶
An exact drop-in for datasketch 2.0.0's MinHash family, with the work in Rust.
from pyroid.datasketch import MinHash, MinHashLSH # instead of: from datasketch import ...
Hash values, LeanMinHash bytes, the LSH (b, r) parameters and query results
are datasketch 2.0.0's, for all three permutation schemes. The submodules mirror
datasketch's (pyroid.datasketch.minhash, .lean_minhash, .lsh, ...), so
from datasketch.lsh import MinHashLSH becomes
from pyroid.datasketch.lsh import MinHashLSH. numpy is required, as for
datasketch; scipy is not.
pyroid extensions run on pyroid's thread pool: MinHash.bulk(..., threads=),
minhash_batch (a signature matrix from token lists or texts) and the
MinHashLSH batch methods.
datasketch classes that pyroid does not port (bBitMinHash, HNSW,
MinHashLSHBloom, AsyncMinHashLSH, WeightedMinHash,
WeightedMinHashGenerator, HyperLogLog, HyperLogLogPlusPlus) are
datasketch's own: getting one from this module imports datasketch, with a
one-time pyroid.FallbackWarning (pyroid.PyroidError if
datasketch is not installed).
LeanMinHash ¶
LeanMinHash(
minhash: MinHash | None = None,
seed: int | None = None,
hashvalues: Iterable[int] | None = None,
scheme: str | None = None,
)
Bases: MinHash
A MinHash with its state frozen: no update.
Parameters:
-
minhash(MinHash | None, default:None) –The MinHash to take the seed, hash values and scheme from.
-
seed(int | None, default:None) –With
hashvaluesandscheme: the seed of the permutations. -
hashvalues(Iterable[int] | None, default:None) –With
seedandscheme: the hash values. -
scheme(str | None, default:None) –The scheme of
hashvalues(required with them;"legacy"for values from datasketch before 2.0.0). Withminhashit may only repeat the MinHash's scheme.
Raises:
-
ValueError–Neither
minhashnorseedandhashvalues; no or conflictingscheme; emptyhashvaluesfor an affine scheme.
bytesize ¶
Return the size of the serialized form in bytes.
Parameters:
-
byteorder(str, default:'@') –A
structbyte order character:@(native, the default),=,<,>or!.
serialize ¶
serialize(buf: Buffer, byteorder: str = '@') -> None
Write this LeanMinHash into the writable buffer buf, from offset 0.
Layout ("legacy", as before datasketch 2.0.0): seed (8 bytes), the
number of hash values (4 bytes), then each value in 4 bytes. Affine
schemes: seed, the negated number of values, a scheme code byte (1 for
"affine32", 2 for "affine64"), then each value in 4 or 8 bytes.
Raises:
-
ValueError–bufis shorter thanbytesize.
deserialize
classmethod
¶
deserialize(
buf: Buffer, byteorder: str = "@"
) -> LeanMinHash
Read a LeanMinHash written by serialize (pyroid's or datasketch's).
Buffers written before datasketch 2.0.0 come back with
scheme="legacy".
Raises:
-
ValueError–An unknown scheme code.
union
classmethod
¶
union(*lmhs: LeanMinHash) -> LeanMinHash
Return a new LeanMinHash, the union of lmhs (at least two).
MinHashLSH ¶
MinHashLSH(
threshold: float = 0.9,
num_perm: int = 128,
weights: tuple[float, float] = (0.5, 0.5),
params: tuple[int, int] | None = None,
storage_config: dict[str, Any] | None = None,
prepickle: bool | None = None,
hashfunc: Callable[[bytes], bytes] | None = None,
)
The MinHash LSH index, for queries with a Jaccard similarity threshold.
Parameters:
-
threshold(float, default:0.9) –The Jaccard similarity threshold, in
[0.0, 1.0]. The band layout is chosen to minimize the weighted false positive and false negative probabilities at this threshold. -
num_perm(int, default:128) –The number of permutations of the MinHashes to index (the
sample_sizeof a weighted MinHash). -
weights(tuple[float, float], default:(0.5, 0.5)) –(false_positive_weight, false_negative_weight); they sum to 1.0. -
params(tuple[int, int] | None, default:None) –(b, r), the number of bands and rows per band. Skips the search;thresholdandweightsare then ignored. -
storage_config(dict[str, Any] | None, default:None) –{"type": "dict"}(the default) is the in-memory index."redis"and"cassandra"are delegated to datasketch."basename"names the storage. -
prepickle(bool | None, default:None) –Pickle keys to bytes before storing them. Default: true for Redis storage only.
-
hashfunc(Callable[[bytes], bytes] | None, default:None) –Hash function applied to band keys. Delegated to datasketch.
Raises:
-
ValueError–For the same invalid arguments as datasketch, with its messages (
"Unknown storage type: ..."included).
Attributes:
-
h(int) –num_perm. -
b(int) –The number of bands (band tables).
-
r(int) –The number of rows per band.
-
hashranges(list[tuple[int, int]]) –[(i * r, (i + 1) * r) for i in range(b)]. -
prepickle(bool) –Whether keys are pickled before they are stored.
-
hashfunc(Callable[[bytes], bytes] | None) –None(an index with a hash function is datasketch's). -
hashtables(list[UnorderedStorage]) –One view per band: band key -> set of keys.
-
keys(OrderedStorage) –View: key -> its band keys.
buffer_size
property
writable
¶
buffer_size: int
The storage buffer size (used by Redis and Cassandra storage).
insert ¶
Insert key with the MinHash (or weighted MinHash) of its set.
Parameters:
-
key(Hashable) –The key of the set (any hashable; keys compare with
==). -
minhash(_Sketch) –Its MinHash, of length
num_perm. -
check_duplication(bool, default:True) –Raise if
keyis already in the index. WithFalse, inserting a key again adds the new band keys to its entry, as datasketch does.
Raises:
-
ValueError–The MinHash has the wrong length or another permutation scheme than earlier ones, or
keyexists.
insert_batch ¶
insert_batch(
keys: Iterable[Hashable],
minhashes: Sequence[_Sketch] | Any,
*,
check_duplication: bool = True,
threads: int | None = None,
) -> None
Insert keys[i] with minhashes[i] for every i (pyroid extension).
The index ends up as after insert of each item in order, but
nothing is inserted if an item is invalid: the error is the one
insert would raise for the first invalid item, with a note naming
it. Band tables are filled in parallel.
Parameters:
-
keys(Iterable[Hashable]) –The keys.
-
minhashes(Sequence[_Sketch] | Any) –MinHash-like objects, or a numpy array whose rows (axis 0) are
hashvaluesarrays, e.g.(n, num_perm)uint64oruint32. Array rows have noscheme, so they are not checked against it (as weighted MinHashes). -
check_duplication(bool, default:True) –As for
insert; a key repeated within the batch is a duplicate too. -
threads(int | None, default:None) –At most this many tasks at once on pyroid's thread pool (
None: the whole pool,1: the calling thread).
Raises:
-
ValueError–keysandminhashesdiffer in length, or asinsert. -
TypeError–An array of another dtype than integers or floats.
merge ¶
merge(
other: MinHashLSH, check_overlap: bool = False
) -> None
Merge other into this index, making this one the union of both.
Only num_perm, b and r are compared, as in datasketch.
Parameters:
-
other(MinHashLSH) –Another pyroid
MinHashLSH. -
check_overlap(bool, default:False) –Raise if a key is in both indexes (nothing is merged then).
Raises:
-
ValueError–The indexes differ in type, parameters or MinHash scheme, or
check_overlapfound a common key.
insertion_session ¶
insertion_session(
buffer_size: int = 50000,
) -> MinHashLSHInsertionSession
Return a context manager whose insert inserts into this index.
deletion_session ¶
deletion_session(
buffer_size: int = 50000,
) -> MinHashLSHDeletionSession
Return a context manager whose remove removes from this index.
query ¶
Return the keys whose sets likely have a Jaccard similarity above the threshold.
Parameters:
-
minhash(_Sketch) –The MinHash of the query set.
Returns:
Raises:
-
ValueError–The MinHash has the wrong length or another scheme.
query_batch ¶
query_batch(
minhashes: Sequence[_Sketch] | Any,
*,
threads: int | None = None,
) -> list[list[Hashable]]
Return [self.query(m) for m in minhashes], in parallel (pyroid extension).
Parameters:
-
minhashes(Sequence[_Sketch] | Any) –MinHash-like objects, or a numpy array of signatures (rows), as for
insert_batch. -
threads(int | None, default:None) –As for
insert_batch.
add_to_query_buffer ¶
add_to_query_buffer(minhash: _Sketch) -> None
Buffer a query for collect_query_buffer.
The buckets are read now (a snapshot), not when the buffer is collected.
collect_query_buffer ¶
Run the buffered queries; return the keys every one of them found.
get_counts ¶
Return, per band table, {band key: number of keys in the bucket}.
MinHashLSHEnsemble ¶
MinHashLSHEnsemble(
threshold: float = 0.9,
num_perm: int = 128,
num_part: int = 16,
m: int = 8,
weights: tuple[float, float] = (0.5, 0.5),
storage_config: dict[str, Any] | None = None,
prepickle: bool | None = None,
)
The LSH Ensemble index for containment queries.
Parameters:
-
threshold(float, default:0.9) –The containment threshold, in [0.0, 1.0].
-
num_perm(int, default:128) –The number of permutations of the indexed MinHash.
-
num_part(int, default:16) –The number of partitions by set size.
-
m(int, default:8) –The maximum band size
rover the inner LSH indexes, in[2, num_perm]. -
weights(tuple[float, float], default:(0.5, 0.5)) –(false_positive_weight, false_negative_weight), summing to 1.0. -
storage_config(dict[str, Any] | None, default:None) –As for
pyroid.datasketch.MinHashLSH. -
prepickle(bool | None, default:None) –As for
pyroid.datasketch.MinHashLSH.
Raises:
-
ValueError–As datasketch, for parameters out of range.
index ¶
Index every (key, minhash, size) of entries; callable once.
Raises:
-
ValueError–The index is not empty,
entriesis empty, or a size is not positive.
query ¶
MinHashLSHForest ¶
The LSH Forest for MinHash: top-k queries by Jaccard similarity.
Band keys are kept in sorted arrays, one per prefix tree, as in datasketch. It also works with weighted MinHashes.
Parameters:
-
num_perm(int, default:128) –The number of permutations of the MinHashes to index (the
sample_sizeof a weighted MinHash). -
l(int, default:8) –The number of prefix trees.
Raises:
-
ValueError–num_permorlis not positive, orl > num_perm.
Attributes:
-
l(int) –The number of prefix trees.
-
k(int) –The maximum depth of a tree,
int(num_perm / l). -
hashranges(list[tuple[int, int]]) –[(i * k, (i + 1) * k) for i in range(l)]. -
keys(MutableMapping[Hashable, list[bytes]]) –Key -> its band keys (a dict view).
-
hashtables(list[Mapping[bytes, list[Hashable]]]) –Per tree: band key -> keys added with it, in order.
-
sorted_hashtables(list[Sequence[bytes]]) –Per tree: its band keys, sorted at the last
index.
add ¶
add(key: Hashable, minhash: _Sketch) -> None
Add key with the MinHash of its set.
The key is searchable after the next index (or now, through
band keys that are already indexed).
Raises:
-
ValueError–The MinHash is shorter than
k * l, has another scheme than earlier ones, orkeywas already added.
query ¶
Return up to k keys with (approximately) the highest Jaccard similarity.
Parameters:
-
minhash(_Sketch) –The MinHash of the query set.
-
k(int) –The maximum number of keys to return.
Returns:
Raises:
-
ValueError–k <= 0, or the MinHash is too short or has another scheme.
get_minhash_hashvalues ¶
Return the hash values stored for key (a numpy array).
Raises:
-
KeyError–keyis not in the forest. -
ValueError–The stored band keys are not whole 4- or 8-byte values.
MinHash ¶
MinHash(
num_perm: int = 128,
seed: int = 1,
gpu_mode: Literal[
"disable", "detect", "always"
] = "disable",
hashfunc: Callable[[Any], Any] | None = None,
hashobj: object | None = None,
hashvalues: ArrayLike | None = None,
permutations: tuple[ArrayLike, ArrayLike]
| ArrayLike
| None = None,
scheme: _Scheme | None = None,
)
MinHash: estimate the Jaccard similarity and cardinality of sets.
Parameters:
-
num_perm(int, default:128) –Number of random permutation functions. Ignored if
hashvaluesis given. -
seed(int, default:1) –The random seed of the permutation functions.
-
gpu_mode(Literal['disable', 'detect', 'always'], default:'disable') –"disable","detect"or"always". pyroid computes on the CPU in every mode;"always"raisesRuntimeErrorif CuPy and a CUDA device are not available, as in datasketch. -
hashfunc(Callable[[Any], Any] | None, default:None) –The hash function: takes a value given to
updateand returns an int of 32 bits ("affine32","legacy") or 64 bits ("affine64"). Default:sha1_hash32, orsha1_hash64for"affine64". Other functions run in Python (slow path). -
hashobj(object | None, default:None) –Deprecated since datasketch 1.4.0; a no-op that warns.
-
hashvalues(ArrayLike | None, default:None) –Initial hash values (another MinHash's
hashvalues).schememust then be given. -
permutations(tuple[ArrayLike, ArrayLike] | ArrayLike | None, default:None) –Permutation parameters (another MinHash's
permutations).schememust then be given. -
scheme(_Scheme | None, default:None) –"affine32"(the default),"affine64"or"legacy"(datasketch before 2.0.0). MinHash of different schemes cannot be compared, merged or unioned.
Raises:
-
ValueError–As datasketch: an unknown scheme,
num_permout of range,hashvalues/permutationswithoutscheme, mismatched lengths, values out of the scheme's range.
update_batch ¶
Add many values to the set (faster than update per value).
Parameters:
-
b(Iterable[Any]) –The values, each passed to
hashfunc. An empty batch leaves the hash values unchanged.
Raises:
-
RuntimeError–gpu_mode="always"without CuPy and a CUDA device.
jaccard ¶
Estimate the Jaccard similarity with other.
Raises:
-
ValueError–Different schemes, seeds or numbers of permutations.
merge ¶
merge(other: MinHash) -> None
Make this MinHash the union of itself and other.
Raises:
-
ValueError–Different schemes, seeds or numbers of permutations.
union
classmethod
¶
Return a new MinHash, the union of mhs (at least two).
Raises:
-
ValueError–Fewer than two, or different seeds, sizes or schemes.
bulk
classmethod
¶
bulk(
b: Iterable[Iterable[Any]],
*,
threads: int | None = None,
**minhash_kwargs: Any,
) -> list[MinHash]
Compute one MinHash per document of b.
Equal to [m.copy() updated with update_batch(doc) for doc in b] with
m = MinHash(**minhash_kwargs), as in datasketch. Documents that are
lists or tuples of bytes are hashed in parallel on pyroid's thread
pool; others run datasketch's code.
Parameters:
-
b(Iterable[Iterable[Any]]) –The documents, each an iterable of values for
hashfunc. -
threads(int | None, default:None) –pyroid extension: at most this many threads (
None: the whole pool,1: the calling thread). -
**minhash_kwargs(Any, default:{}) –Arguments for
MinHash, used for all.
Example
data = [[b"token1", b"token2", b"token3"], [b"token4", b"token5"]]
minhashes = MinHash.bulk(data, num_perm=64)
generator
classmethod
¶
generator(
b: Iterable[Iterable[Any]],
*,
threads: int | None = None,
**minhash_kwargs: Any,
) -> Generator[MinHash]
Yield one MinHash per document of b (see bulk).
pyroid reads up to 2,048 documents ahead to hash them in parallel; the MinHash objects and any error come out in datasketch's order.
sha1_hash32 ¶
sha1_hash32(data: Buffer) -> int
Return the first 4 bytes of sha1(data) as a little-endian int.
sha1_hash64 ¶
sha1_hash64(data: Buffer) -> int
Return the first 8 bytes of sha1(data) as a little-endian int.
minhash_batch ¶
minhash_batch(
docs: str
| list[str]
| tuple[str, ...]
| ArrowStrings
| Sequence[Sequence[bytes | str]],
*,
num_perm: int = 128,
seed: int = 1,
scheme: _Scheme | None = None,
ngram: int = 1,
threads: int | None = None,
) -> NDArray[uint32] | NDArray[uint64]
Compute MinHash signatures for many documents, as a matrix.
Row i equals MinHash(num_perm, seed, scheme=scheme) updated with the
tokens of document i, i.e. its MinHash.hashvalues (uint32 for
"affine32", uint64 otherwise). Documents are hashed in parallel on
pyroid's thread pool. A pyroid extension.
Parameters:
-
docs(str | list[str] | tuple[str, ...] | ArrowStrings | Sequence[Sequence[bytes | str]]) –Token lists (a list or tuple of lists/tuples of
bytesorstrtokens;stris hashed as UTF-8), or texts (str,list[str],tuple[str, ...]or an Arrow string array). A text's tokens are its wordngram-grams:" ".join(words[i:i + ngram])overtext.split(), UTF-8 encoded; a text with fewer words has no tokens. Arrow nulls give the empty signature. -
num_perm(int, default:128) –Number of permutations.
-
seed(int, default:1) –The permutation seed.
-
scheme(_Scheme | None, default:None) –"affine32"(default),"affine64"or"legacy". -
ngram(int, default:1) –Words per shingle, for texts only.
-
threads(int | None, default:None) –At most this many threads (
None: the whole pool).
Returns:
-
NDArray[uint32] | NDArray[uint64]–A
(len(docs), num_perm)array, or(num_perm,)for a singlestr.
Raises:
-
InputError–A document or token of an unsupported type.
-
ValueError–ngrambelow 1, or asMinHashfor bad parameters. -
UnicodeEncodeError–A
strwith a lone surrogate.
Example
from pyroid.datasketch import MinHashLSH, minhash_batch
sigs = minhash_batch(texts, ngram=5, threads=8) # (len(texts), 128) uint32
lsh = MinHashLSH(threshold=0.8)
lsh.insert_batch(range(len(texts)), sigs)
pyroid.datasketch.minhash¶
datasketch's MinHash (datasketch.minhash), with the hashing in Rust.
from pyroid.datasketch import MinHash replaces from datasketch import MinHash.
A pyroid MinHash has datasketch 2.0.0's attributes (hashvalues,
permutations, seed, scheme, hashfunc, ...), the same hash values
for all three permutation schemes ("affine32", the default, "affine64" and
"legacy"), the same pickled state and the same errors.
What runs in Rust: drawing the permutation parameters (numpy's RandomState,
reproduced and cached per seed), SHA1 hashing with the default hash functions,
and applying the permutations. MinHash.bulk and MinHash.generator
hash many documents in parallel (threads=), and minhash_batch returns
a signature matrix for token lists or the word n-grams of texts.
Example
from pyroid.datasketch import MinHash
m1, m2 = MinHash(num_perm=128), MinHash(num_perm=128)
m1.update_batch([b"minhash", b"is", b"a", b"probabilistic", b"data", b"structure"])
m2.update_batch([b"minhash", b"is", b"a", b"probability", b"data", b"structure"])
m1.jaccard(m2) # 0.65625, as with datasketch
sigs = MinHash.bulk([[b"a", b"b"], [b"b", b"c"]], num_perm=64, threads=8)
Differences from datasketch¶
- A
hashfuncother thansha1_hash32/sha1_hash64(pyroid's or datasketch's) is called from Python for every token, as in datasketch, with a one-timepyroid.FallbackWarning; the permutations still run in Rust when it returns ints in range. - Tokens that are not
bytes(bytearray,memoryview, numpy scalars), and hash values or permutations that are not the scheme's numpy dtype, run datasketch's numpy code (same results, datasketch's speed). gpu_modeis accepted, but pyroid always computes on the CPU; the values are the same.gpu_mode="always"still raisesRuntimeErrorwithout CuPy and a CUDA device, as datasketch does.- CuPy is imported only when a MinHash with
gpu_modeother than"disable"updates, not when the module is imported.
MinHash ¶
MinHash(
num_perm: int = 128,
seed: int = 1,
gpu_mode: Literal[
"disable", "detect", "always"
] = "disable",
hashfunc: Callable[[Any], Any] | None = None,
hashobj: object | None = None,
hashvalues: ArrayLike | None = None,
permutations: tuple[ArrayLike, ArrayLike]
| ArrayLike
| None = None,
scheme: _Scheme | None = None,
)
MinHash: estimate the Jaccard similarity and cardinality of sets.
Parameters:
-
num_perm(int, default:128) –Number of random permutation functions. Ignored if
hashvaluesis given. -
seed(int, default:1) –The random seed of the permutation functions.
-
gpu_mode(Literal['disable', 'detect', 'always'], default:'disable') –"disable","detect"or"always". pyroid computes on the CPU in every mode;"always"raisesRuntimeErrorif CuPy and a CUDA device are not available, as in datasketch. -
hashfunc(Callable[[Any], Any] | None, default:None) –The hash function: takes a value given to
updateand returns an int of 32 bits ("affine32","legacy") or 64 bits ("affine64"). Default:sha1_hash32, orsha1_hash64for"affine64". Other functions run in Python (slow path). -
hashobj(object | None, default:None) –Deprecated since datasketch 1.4.0; a no-op that warns.
-
hashvalues(ArrayLike | None, default:None) –Initial hash values (another MinHash's
hashvalues).schememust then be given. -
permutations(tuple[ArrayLike, ArrayLike] | ArrayLike | None, default:None) –Permutation parameters (another MinHash's
permutations).schememust then be given. -
scheme(_Scheme | None, default:None) –"affine32"(the default),"affine64"or"legacy"(datasketch before 2.0.0). MinHash of different schemes cannot be compared, merged or unioned.
Raises:
-
ValueError–As datasketch: an unknown scheme,
num_permout of range,hashvalues/permutationswithoutscheme, mismatched lengths, values out of the scheme's range.
update_batch ¶
Add many values to the set (faster than update per value).
Parameters:
-
b(Iterable[Any]) –The values, each passed to
hashfunc. An empty batch leaves the hash values unchanged.
Raises:
-
RuntimeError–gpu_mode="always"without CuPy and a CUDA device.
jaccard ¶
Estimate the Jaccard similarity with other.
Raises:
-
ValueError–Different schemes, seeds or numbers of permutations.
merge ¶
merge(other: MinHash) -> None
Make this MinHash the union of itself and other.
Raises:
-
ValueError–Different schemes, seeds or numbers of permutations.
union
classmethod
¶
Return a new MinHash, the union of mhs (at least two).
Raises:
-
ValueError–Fewer than two, or different seeds, sizes or schemes.
bulk
classmethod
¶
bulk(
b: Iterable[Iterable[Any]],
*,
threads: int | None = None,
**minhash_kwargs: Any,
) -> list[MinHash]
Compute one MinHash per document of b.
Equal to [m.copy() updated with update_batch(doc) for doc in b] with
m = MinHash(**minhash_kwargs), as in datasketch. Documents that are
lists or tuples of bytes are hashed in parallel on pyroid's thread
pool; others run datasketch's code.
Parameters:
-
b(Iterable[Iterable[Any]]) –The documents, each an iterable of values for
hashfunc. -
threads(int | None, default:None) –pyroid extension: at most this many threads (
None: the whole pool,1: the calling thread). -
**minhash_kwargs(Any, default:{}) –Arguments for
MinHash, used for all.
Example
data = [[b"token1", b"token2", b"token3"], [b"token4", b"token5"]]
minhashes = MinHash.bulk(data, num_perm=64)
generator
classmethod
¶
generator(
b: Iterable[Iterable[Any]],
*,
threads: int | None = None,
**minhash_kwargs: Any,
) -> Generator[MinHash]
Yield one MinHash per document of b (see bulk).
pyroid reads up to 2,048 documents ahead to hash them in parallel; the MinHash objects and any error come out in datasketch's order.
minhash_batch ¶
minhash_batch(
docs: str
| list[str]
| tuple[str, ...]
| ArrowStrings
| Sequence[Sequence[bytes | str]],
*,
num_perm: int = 128,
seed: int = 1,
scheme: _Scheme | None = None,
ngram: int = 1,
threads: int | None = None,
) -> NDArray[uint32] | NDArray[uint64]
Compute MinHash signatures for many documents, as a matrix.
Row i equals MinHash(num_perm, seed, scheme=scheme) updated with the
tokens of document i, i.e. its MinHash.hashvalues (uint32 for
"affine32", uint64 otherwise). Documents are hashed in parallel on
pyroid's thread pool. A pyroid extension.
Parameters:
-
docs(str | list[str] | tuple[str, ...] | ArrowStrings | Sequence[Sequence[bytes | str]]) –Token lists (a list or tuple of lists/tuples of
bytesorstrtokens;stris hashed as UTF-8), or texts (str,list[str],tuple[str, ...]or an Arrow string array). A text's tokens are its wordngram-grams:" ".join(words[i:i + ngram])overtext.split(), UTF-8 encoded; a text with fewer words has no tokens. Arrow nulls give the empty signature. -
num_perm(int, default:128) –Number of permutations.
-
seed(int, default:1) –The permutation seed.
-
scheme(_Scheme | None, default:None) –"affine32"(default),"affine64"or"legacy". -
ngram(int, default:1) –Words per shingle, for texts only.
-
threads(int | None, default:None) –At most this many threads (
None: the whole pool).
Returns:
-
NDArray[uint32] | NDArray[uint64]–A
(len(docs), num_perm)array, or(num_perm,)for a singlestr.
Raises:
-
InputError–A document or token of an unsupported type.
-
ValueError–ngrambelow 1, or asMinHashfor bad parameters. -
UnicodeEncodeError–A
strwith a lone surrogate.
Example
from pyroid.datasketch import MinHashLSH, minhash_batch
sigs = minhash_batch(texts, ngram=5, threads=8) # (len(texts), 128) uint32
lsh = MinHashLSH(threshold=0.8)
lsh.insert_batch(range(len(texts)), sigs)
pyroid.datasketch.lean_minhash¶
datasketch's LeanMinHash (datasketch.lean_minhash).
A frozen pyroid.datasketch.MinHash without permutations or hash
function, for storage and indexing. LeanMinHash.serialize writes
datasketch 2.0.0's byte format (the pre-2.0.0 format for "legacy"), so the
bytes are interchangeable with datasketch's in both directions; pickles hold
the same bytes.
Example
from pyroid.datasketch import LeanMinHash
lean = LeanMinHash(minhash)
buf = bytearray(lean.bytesize())
lean.serialize(buf)
LeanMinHash.deserialize(buf) == lean # True
LeanMinHash ¶
LeanMinHash(
minhash: MinHash | None = None,
seed: int | None = None,
hashvalues: Iterable[int] | None = None,
scheme: str | None = None,
)
Bases: MinHash
A MinHash with its state frozen: no update.
Parameters:
-
minhash(MinHash | None, default:None) –The MinHash to take the seed, hash values and scheme from.
-
seed(int | None, default:None) –With
hashvaluesandscheme: the seed of the permutations. -
hashvalues(Iterable[int] | None, default:None) –With
seedandscheme: the hash values. -
scheme(str | None, default:None) –The scheme of
hashvalues(required with them;"legacy"for values from datasketch before 2.0.0). Withminhashit may only repeat the MinHash's scheme.
Raises:
-
ValueError–Neither
minhashnorseedandhashvalues; no or conflictingscheme; emptyhashvaluesfor an affine scheme.
bytesize ¶
Return the size of the serialized form in bytes.
Parameters:
-
byteorder(str, default:'@') –A
structbyte order character:@(native, the default),=,<,>or!.
serialize ¶
serialize(buf: Buffer, byteorder: str = '@') -> None
Write this LeanMinHash into the writable buffer buf, from offset 0.
Layout ("legacy", as before datasketch 2.0.0): seed (8 bytes), the
number of hash values (4 bytes), then each value in 4 bytes. Affine
schemes: seed, the negated number of values, a scheme code byte (1 for
"affine32", 2 for "affine64"), then each value in 4 or 8 bytes.
Raises:
-
ValueError–bufis shorter thanbytesize.
deserialize
classmethod
¶
deserialize(
buf: Buffer, byteorder: str = "@"
) -> LeanMinHash
Read a LeanMinHash written by serialize (pyroid's or datasketch's).
Buffers written before datasketch 2.0.0 come back with
scheme="legacy".
Raises:
-
ValueError–An unknown scheme code.
union
classmethod
¶
union(*lmhs: LeanMinHash) -> LeanMinHash
Return a new LeanMinHash, the union of lmhs (at least two).
pyroid.datasketch.lsh¶
MinHash LSH (pyroid.datasketch.lsh), as datasketch 2.0.0's datasketch.lsh.
MinHashLSH has datasketch's constructor, methods, attributes and error
messages, and returns the same query results (as sets: result lists are in
no particular order, as in datasketch). The in-memory index is in Rust:
from pyroid.datasketch.lsh import MinHashLSH
lsh = MinHashLSH(threshold=0.5, num_perm=128)
for key, m in minhashes.items():
lsh.insert(key, m)
lsh.query(minhashes["doc-1"])
lsh.insert_batch(keys, list_of_minhashes, threads=8) # pyroid extension
lsh.query_batch(signatures_2d_array) # pyroid extension
The index reads hashvalues, len() and scheme of the sketches it is
given, so datasketch's MinHash, LeanMinHash and WeightedMinHash
work as well as pyroid's. Band keys are computed in Rust when hashvalues
is a C-contiguous numpy array of an integer or float dtype in native byte
order; anything else runs datasketch's own expression
bytes(hashvalues[start:end].byteswap().data).
Differences from datasketch:
storage_configof type"redis"or"cassandra", and ahashfunc, are handled by datasketch:MinHashLSH(...)returns adatasketch.MinHashLSHand emits apyroid.FallbackWarning(once).keysandhashtables[i]are read-only views (pyroid.datasketch.storage.DictListStorageView,pyroid.datasketch.storage.DictSetStorageView). Their values are new lists and sets on every access (a snapshot), so a query buffered withMinHashLSH.add_to_query_buffersees the index as it was then. The mutating storage methods raiseNotImplementedError. Band tables iterate in no particular order (keysiterates in insertion order, as a dict).- When several equal keys of different types are involved (
1,1.0,True), the object returned may be another one of the equal keys than datasketch returns.
The (b, r) search for threshold and weights is a Rust port of
datasketch's (scipy.integrate.quad) and picks the same parameters; it
needs neither datasketch nor scipy.
MinHashLSH ¶
MinHashLSH(
threshold: float = 0.9,
num_perm: int = 128,
weights: tuple[float, float] = (0.5, 0.5),
params: tuple[int, int] | None = None,
storage_config: dict[str, Any] | None = None,
prepickle: bool | None = None,
hashfunc: Callable[[bytes], bytes] | None = None,
)
The MinHash LSH index, for queries with a Jaccard similarity threshold.
Parameters:
-
threshold(float, default:0.9) –The Jaccard similarity threshold, in
[0.0, 1.0]. The band layout is chosen to minimize the weighted false positive and false negative probabilities at this threshold. -
num_perm(int, default:128) –The number of permutations of the MinHashes to index (the
sample_sizeof a weighted MinHash). -
weights(tuple[float, float], default:(0.5, 0.5)) –(false_positive_weight, false_negative_weight); they sum to 1.0. -
params(tuple[int, int] | None, default:None) –(b, r), the number of bands and rows per band. Skips the search;thresholdandweightsare then ignored. -
storage_config(dict[str, Any] | None, default:None) –{"type": "dict"}(the default) is the in-memory index."redis"and"cassandra"are delegated to datasketch."basename"names the storage. -
prepickle(bool | None, default:None) –Pickle keys to bytes before storing them. Default: true for Redis storage only.
-
hashfunc(Callable[[bytes], bytes] | None, default:None) –Hash function applied to band keys. Delegated to datasketch.
Raises:
-
ValueError–For the same invalid arguments as datasketch, with its messages (
"Unknown storage type: ..."included).
Attributes:
-
h(int) –num_perm. -
b(int) –The number of bands (band tables).
-
r(int) –The number of rows per band.
-
hashranges(list[tuple[int, int]]) –[(i * r, (i + 1) * r) for i in range(b)]. -
prepickle(bool) –Whether keys are pickled before they are stored.
-
hashfunc(Callable[[bytes], bytes] | None) –None(an index with a hash function is datasketch's). -
hashtables(list[UnorderedStorage]) –One view per band: band key -> set of keys.
-
keys(OrderedStorage) –View: key -> its band keys.
buffer_size
property
writable
¶
buffer_size: int
The storage buffer size (used by Redis and Cassandra storage).
insert ¶
Insert key with the MinHash (or weighted MinHash) of its set.
Parameters:
-
key(Hashable) –The key of the set (any hashable; keys compare with
==). -
minhash(_Sketch) –Its MinHash, of length
num_perm. -
check_duplication(bool, default:True) –Raise if
keyis already in the index. WithFalse, inserting a key again adds the new band keys to its entry, as datasketch does.
Raises:
-
ValueError–The MinHash has the wrong length or another permutation scheme than earlier ones, or
keyexists.
insert_batch ¶
insert_batch(
keys: Iterable[Hashable],
minhashes: Sequence[_Sketch] | Any,
*,
check_duplication: bool = True,
threads: int | None = None,
) -> None
Insert keys[i] with minhashes[i] for every i (pyroid extension).
The index ends up as after insert of each item in order, but
nothing is inserted if an item is invalid: the error is the one
insert would raise for the first invalid item, with a note naming
it. Band tables are filled in parallel.
Parameters:
-
keys(Iterable[Hashable]) –The keys.
-
minhashes(Sequence[_Sketch] | Any) –MinHash-like objects, or a numpy array whose rows (axis 0) are
hashvaluesarrays, e.g.(n, num_perm)uint64oruint32. Array rows have noscheme, so they are not checked against it (as weighted MinHashes). -
check_duplication(bool, default:True) –As for
insert; a key repeated within the batch is a duplicate too. -
threads(int | None, default:None) –At most this many tasks at once on pyroid's thread pool (
None: the whole pool,1: the calling thread).
Raises:
-
ValueError–keysandminhashesdiffer in length, or asinsert. -
TypeError–An array of another dtype than integers or floats.
merge ¶
merge(
other: MinHashLSH, check_overlap: bool = False
) -> None
Merge other into this index, making this one the union of both.
Only num_perm, b and r are compared, as in datasketch.
Parameters:
-
other(MinHashLSH) –Another pyroid
MinHashLSH. -
check_overlap(bool, default:False) –Raise if a key is in both indexes (nothing is merged then).
Raises:
-
ValueError–The indexes differ in type, parameters or MinHash scheme, or
check_overlapfound a common key.
insertion_session ¶
insertion_session(
buffer_size: int = 50000,
) -> MinHashLSHInsertionSession
Return a context manager whose insert inserts into this index.
deletion_session ¶
deletion_session(
buffer_size: int = 50000,
) -> MinHashLSHDeletionSession
Return a context manager whose remove removes from this index.
query ¶
Return the keys whose sets likely have a Jaccard similarity above the threshold.
Parameters:
-
minhash(_Sketch) –The MinHash of the query set.
Returns:
Raises:
-
ValueError–The MinHash has the wrong length or another scheme.
query_batch ¶
query_batch(
minhashes: Sequence[_Sketch] | Any,
*,
threads: int | None = None,
) -> list[list[Hashable]]
Return [self.query(m) for m in minhashes], in parallel (pyroid extension).
Parameters:
-
minhashes(Sequence[_Sketch] | Any) –MinHash-like objects, or a numpy array of signatures (rows), as for
insert_batch. -
threads(int | None, default:None) –As for
insert_batch.
add_to_query_buffer ¶
add_to_query_buffer(minhash: _Sketch) -> None
Buffer a query for collect_query_buffer.
The buckets are read now (a snapshot), not when the buffer is collected.
collect_query_buffer ¶
Run the buffered queries; return the keys every one of them found.
get_counts ¶
Return, per band table, {band key: number of keys in the bucket}.
MinHashLSHInsertionSession ¶
MinHashLSHInsertionSession(
lsh: MinHashLSH, buffer_size: int
)
Context manager for inserting many keys into a MinHashLSH.
insert ¶
Insert key with its MinHash, as MinHashLSH.insert.
MinHashLSHDeletionSession ¶
MinHashLSHDeletionSession(
lsh: MinHashLSH, buffer_size: int
)
Context manager for removing many keys from a MinHashLSH.
pyroid.datasketch.lshforest¶
MinHash LSH Forest (pyroid.datasketch.lshforest), as datasketch 2.0.0's.
MinHashLSHForest has datasketch's methods, attributes and errors, and
query(minhash, k) returns the same key set as datasketch, including where
it stops at k keys: buckets and their keys are visited in datasketch's
order. The index is in Rust:
from pyroid.datasketch.lshforest import MinHashLSHForest
forest = MinHashLSHForest(num_perm=128)
for key, m in minhashes.items():
forest.add(key, m)
forest.index()
forest.query(minhashes["doc-1"], 10)
Keys added after MinHashLSHForest.index are found when they share an
indexed band key, as in datasketch.
Differences from datasketch: keys, hashtables[i] and
sorted_hashtables[i] are views of the native index with the API of a
dict, a defaultdict(list) and a list. Values read from them are new
lists. keys accepts assignment and deletion of entries, whose values must
be lists of bytes; reading a missing bucket of hashtables[i] returns
[] without creating it.
MinHashLSHForest ¶
The LSH Forest for MinHash: top-k queries by Jaccard similarity.
Band keys are kept in sorted arrays, one per prefix tree, as in datasketch. It also works with weighted MinHashes.
Parameters:
-
num_perm(int, default:128) –The number of permutations of the MinHashes to index (the
sample_sizeof a weighted MinHash). -
l(int, default:8) –The number of prefix trees.
Raises:
-
ValueError–num_permorlis not positive, orl > num_perm.
Attributes:
-
l(int) –The number of prefix trees.
-
k(int) –The maximum depth of a tree,
int(num_perm / l). -
hashranges(list[tuple[int, int]]) –[(i * k, (i + 1) * k) for i in range(l)]. -
keys(MutableMapping[Hashable, list[bytes]]) –Key -> its band keys (a dict view).
-
hashtables(list[Mapping[bytes, list[Hashable]]]) –Per tree: band key -> keys added with it, in order.
-
sorted_hashtables(list[Sequence[bytes]]) –Per tree: its band keys, sorted at the last
index.
add ¶
add(key: Hashable, minhash: _Sketch) -> None
Add key with the MinHash of its set.
The key is searchable after the next index (or now, through
band keys that are already indexed).
Raises:
-
ValueError–The MinHash is shorter than
k * l, has another scheme than earlier ones, orkeywas already added.
query ¶
Return up to k keys with (approximately) the highest Jaccard similarity.
Parameters:
-
minhash(_Sketch) –The MinHash of the query set.
-
k(int) –The maximum number of keys to return.
Returns:
Raises:
-
ValueError–k <= 0, or the MinHash is too short or has another scheme.
get_minhash_hashvalues ¶
Return the hash values stored for key (a numpy array).
Raises:
-
KeyError–keyis not in the forest. -
ValueError–The stored band keys are not whole 4- or 8-byte values.
pyroid.datasketch.lshensemble¶
LSH Ensemble, as datasketch 2.0.0's datasketch.lshensemble.
MinHashLSHEnsemble answers containment queries: which indexed sets
contain at least threshold of the query set. It is datasketch's class on top
of pyroid's pyroid.datasketch.MinHashLSH, so the partitions, the
(b, r) parameters per partition and the query results are datasketch's.
The parameter search (scipy's quad in datasketch) runs in Rust, ten
searches in parallel, so constructing an ensemble takes milliseconds.
Example
from pyroid.datasketch import MinHashLSHEnsemble
ens = MinHashLSHEnsemble(threshold=0.8, num_perm=128, num_part=16)
ens.index([(key, minhash, size) for key, minhash, size in entries])
set(ens.query(query_minhash, query_size))
Redis and Cassandra storage go through pyroid's MinHashLSH, which hands them
to datasketch (with a one-time pyroid.FallbackWarning).
MinHashLSHEnsemble ¶
MinHashLSHEnsemble(
threshold: float = 0.9,
num_perm: int = 128,
num_part: int = 16,
m: int = 8,
weights: tuple[float, float] = (0.5, 0.5),
storage_config: dict[str, Any] | None = None,
prepickle: bool | None = None,
)
The LSH Ensemble index for containment queries.
Parameters:
-
threshold(float, default:0.9) –The containment threshold, in [0.0, 1.0].
-
num_perm(int, default:128) –The number of permutations of the indexed MinHash.
-
num_part(int, default:16) –The number of partitions by set size.
-
m(int, default:8) –The maximum band size
rover the inner LSH indexes, in[2, num_perm]. -
weights(tuple[float, float], default:(0.5, 0.5)) –(false_positive_weight, false_negative_weight), summing to 1.0. -
storage_config(dict[str, Any] | None, default:None) –As for
pyroid.datasketch.MinHashLSH. -
prepickle(bool | None, default:None) –As for
pyroid.datasketch.MinHashLSH.
Raises:
-
ValueError–As datasketch, for parameters out of range.
index ¶
Index every (key, minhash, size) of entries; callable once.
Raises:
-
ValueError–The index is not empty,
entriesis empty, or a size is not positive.
query ¶
pyroid.datasketch.storage¶
Storage containers (pyroid.datasketch.storage), as datasketch.storage.
Storage, OrderedStorage, UnorderedStorage, DictListStorage,
DictSetStorage, ordered_storage and unordered_storage are
datasketch's in-memory classes and factories. "redis" and "cassandra"
configurations are passed to datasketch's factories (a
pyroid.FallbackWarning).
MinHashLSH keeps its index in Rust; its keys and hashtables[i] are
the read-only views DictListStorageView and
DictSetStorageView, with the same read API. Their values are new
lists and sets on every call (a snapshot).
Storage ¶
Bases: ABC
Base class for key, value containers where the values are sequences.
DictListStorage ¶
DictListStorage(config: Any)
Bases: OrderedStorage
defaultdict(list) with the Storage API.
remove_val ¶
Remove val from the values of key.
DictSetStorage ¶
DictSetStorage(config: Any)
Bases: UnorderedStorage, DictListStorage
defaultdict(set) with the Storage API.
DictListStorageView ¶
DictListStorageView(index: Any)
Bases: OrderedStorage
Read-only view of MinHashLSH.keys: key -> its band keys.
It has DictListStorage's API. insert, remove and
remove_val raise NotImplementedError.
DictSetStorageView ¶
Bases: UnorderedStorage
Read-only view of one band table, MinHashLSH.hashtables[i].
It has DictSetStorage's API: band key (bytes) -> the set of keys in
that bucket. Buckets iterate in no particular order. insert,
remove and remove_val raise NotImplementedError.
get ¶
Return the keys in bucket key (a new set), set() if it is absent.
insert ¶
Raise NotImplementedError: the view is read-only.
remove ¶
Raise NotImplementedError: the view is read-only.
remove_val ¶
Raise NotImplementedError: the view is read-only.
itemcounts ¶
Return {band key: number of keys in the bucket}.
ordered_storage ¶
ordered_storage(
config: dict[str, Any], name: bytes | None = None
) -> OrderedStorage
Return an ordered storage (values are lists) for config.
Raises:
-
ValueError–"Unknown storage type: ...".
unordered_storage ¶
unordered_storage(
config: dict[str, Any], name: bytes | None = None
) -> UnorderedStorage
Return an unordered storage (values are sets) for config.
Raises:
-
ValueError–"Unknown storage type: ...".
pyroid.datasketch.hashfunc¶
datasketch's SHA1-based hash functions (datasketch.hashfunc), unchanged.
pyroid.datasketch.MinHash recognises these two (and datasketch's own)
and hashes natively. Called directly they are plain hashlib code, so they
take and reject what datasketch's take and reject (bytes-like data only).