pyhyperminhash 0.2.0__cp39-abi3-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ from .pyhyperminhash import *
2
+
3
+ __doc__ = pyhyperminhash.__doc__
4
+ if hasattr(pyhyperminhash, "__all__"):
5
+ __all__ = pyhyperminhash.__all__
@@ -0,0 +1,59 @@
1
+ import typing
2
+ from typing_extensions import final
3
+
4
+ class _SupportsRead(typing.Protocol):
5
+ def read(self, n: int, /) -> bytes: ...
6
+
7
+ __all__ = [
8
+ "__version_info__",
9
+ "Entry",
10
+ "Sketch",
11
+ "__version__",
12
+ "__profile__",
13
+ "__hyperminhash_version__",
14
+ ]
15
+
16
+ @final
17
+ class Entry:
18
+ def add(self, obj: typing.Hashable, /) -> None: ...
19
+ def add_bytes(self, obj: bytes, /) -> None: ...
20
+ def __iadd__(self, value: typing.Hashable, /) -> "Entry": ...
21
+ def add_reader(self, src: _SupportsRead, /) -> int: ...
22
+ def fork(self) -> "Entry": ...
23
+ def _digest(self) -> int: ...
24
+
25
+ @final
26
+ class Sketch:
27
+ @staticmethod
28
+ def load(buf: bytes, /) -> "Sketch": ...
29
+ @staticmethod
30
+ def from_iter(src: typing.Iterator[typing.Hashable], /) -> "Sketch": ...
31
+ def save(self) -> bytes: ...
32
+ def add(self, obj: typing.Hashable, /) -> None: ...
33
+ def add_reader(self, src: _SupportsRead, /) -> int: ...
34
+ def add_entry(self, entry: Entry, /) -> None: ...
35
+ def cardinality(self) -> float: ...
36
+ def union(self, other: "Sketch", /) -> None: ...
37
+ def intersection(self, other: "Sketch", /, *, fast: bool = False) -> float: ...
38
+ def similarity(self, other: "Sketch", /, *, fast: bool = False) -> float: ...
39
+ def intersection_many(
40
+ self, others: typing.Iterable["Sketch"], /
41
+ ) -> list[float]: ...
42
+ def similarity_many(self, others: typing.Iterable["Sketch"], /) -> list[float]: ...
43
+ def __bool__(self) -> bool: ...
44
+ def __int__(self) -> int: ...
45
+ def __len__(self) -> int: ...
46
+ def __float__(self) -> float: ...
47
+ def __iadd__(self, value: typing.Hashable, /) -> "Sketch": ...
48
+ def __ior__(self, value: "Sketch", /) -> "Sketch": ...
49
+ def __or__(self, value: "Sketch", /) -> "Sketch": ...
50
+ def __lt__(self, value: "Sketch", /) -> bool: ...
51
+ def __le__(self, value: "Sketch", /) -> bool: ...
52
+ def __gt__(self, value: "Sketch", /) -> bool: ...
53
+ def __ge__(self, value: "Sketch", /) -> bool: ...
54
+
55
+ def __version_info__() -> str: ...
56
+
57
+ __version__: str
58
+ __hyperminhash_version__: str
59
+ __profile__: str
File without changes
Binary file
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyhyperminhash
3
+ Version: 0.2.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ License-File: LICENSE
9
+ Summary: Very fast, constant memory-footprint cardinality approximation, including intersection
10
+ Author-email: Lukas Lueg <lukas.lueg@gmail.com>
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
13
+ Project-URL: Homepage, https://github.com/lukaslueg/pyhyperminhash
14
+
15
+ # Hyperminhash for Python
16
+
17
+ [![Docs](https://docs.rs/hyperminhash/badge.svg)](https://docs.rs/hyperminhash)
18
+ [![PyPI](https://badge.fury.io/py/pyhyperminhash.svg)](https://pypi.org/project/pyhyperminhash/)
19
+
20
+ Very fast, constant memory-footprint cardinality approximation, including intersection
21
+ and union operation.
22
+
23
+ The class `Sketch` can be used to count unique elements that were encountered during
24
+ the instance's lifetime. Unlike e.g. when using a `set`, the memory consumed by the
25
+ `Sketch`-instance does _not_ grow as elements are added; each `Sketch`-instance
26
+ consumes approximately 32kb of memory, independent of the number of elements.
27
+
28
+ ### Installation
29
+
30
+ ```bash
31
+ pip install pyhyperminhash
32
+ ```
33
+
34
+ ### Basic usage
35
+
36
+ ```python
37
+ # Construct an empty Sketch
38
+ sk = pyhyperminhash.Sketch()
39
+ assert not sk # The Sketch is empty
40
+ sk.add('Foo') # Add elements
41
+ sk.add('Bar')
42
+ sk.add(42)
43
+ assert sk # The Sketch is not empty
44
+ assert float(sk) == sk.cardinality() # Approximately 3.0
45
+ assert int(sk) == len(sk) # Approximately 3
46
+ ```
47
+
48
+ ```python
49
+ # Sketches can be combined...
50
+
51
+ sk2 = pyhyperminhash.Sketch()
52
+ sk2.add('Foobar')
53
+ sk2 |= sk # sk2 now holds all elements that were in `sk` or `sk2`
54
+
55
+ sk3 = pyhyperminhash.Sketch()
56
+ sk3.add(42)
57
+ sk3.add('Foo')
58
+ assert sk3.intersection(sk2) > 0.0 # Approximately 1.0, due to `Foo`
59
+
60
+ # `fast=True` uses the faster upstream estimate with a slightly looser
61
+ # correction model; for most inputs the difference is tiny.
62
+ assert sk3.intersection(sk2, fast=True) > 0.0
63
+ assert sk3.similarity(sk2, fast=True) > 0.0
64
+
65
+ # Compare one Sketch against many others
66
+ others = [sk, sk2, sk3]
67
+ assert sk3.intersection_many(others) == [sk3.intersection(other) for other in others]
68
+ assert sk3.similarity_many(others) == [sk3.similarity(other) for other in others]
69
+ ```
70
+
71
+ ```python
72
+ # Sketches can be stored for later use
73
+
74
+ buf = sk.save() # Serialize the Sketch into a bytes-buffer
75
+ new_sk = pyhyperminhash.Sketch.load(buf) # Deserialize a Sketch
76
+ assert len(new_sk) == len(sk)
77
+ ```
78
+
79
+ ```python
80
+ # Sketches can be constructed from iterators directly, which avoids
81
+ # materializing all objects simultaneously.
82
+
83
+ # This is also faster than adding elements one by one.
84
+ sk = pyhyperminhash.Sketch.from_iter(iter(range(100)))
85
+
86
+ # Read all lines; uses binary mode to avoid Unicode encoding+decoding
87
+ with open('complaints.txt', 'rb') as f:
88
+ sk = pyhyperminhash.Sketch.from_iter(f)
89
+
90
+ # Add an entire file-like object
91
+ with open('complaints.txt', 'rb') as f:
92
+ sk.add_reader(f)
93
+ ```
94
+
95
+ ```python
96
+ # Entry-objects can be used to construct what represents a single
97
+ # object from multiple parts. They consume very little memory.
98
+
99
+ # Add sub-objects that don't fit into memory
100
+ e = pyhyperminhash.Entry()
101
+ with open('complaints.txt', 'rb') as f:
102
+ e.add(f.read(4096)) # Add two parts to this Entry
103
+ e.add(f.read(4096))
104
+ sk.add_entry(e) # This counts as one object
105
+
106
+ sk2 = pyhyperminhash.Sketch()
107
+ sk2.add_entry(e) # Entry-objects can be added to multiple Sketches
108
+
109
+ # Entry-objects can be "forked" at the state they are currently at
110
+ e2 = e.fork()
111
+ e2.add("Foo")
112
+ sk.add_entry(e2) # This is a new object in `Sketch`...
113
+
114
+ e3 = e.fork()
115
+ e3.add("Bar")
116
+ sk.add_entry(e3) # So is this, without re-computing `e`
117
+ ```
118
+
119
+ It is very much preferred to provide `bytes` to `Sketch.add()` and `Entry.add()`,
120
+ in order to avoid double-hashing the content. Also, notice that the Python interpreter
121
+ probably randomizes hash-values at interpreter-startup (in order to prevent certain kinds
122
+ of DOS-attacks). The randomness introduced here means that the count-approximation provided
123
+ by this package may not be stable from run to run.
124
+
125
+ See the documentation for the underlying [implementation](https://docs.rs/hyperminhash) for additional information.
126
+
127
+ ### Usage as a sqlite3 aggregate function
128
+
129
+ ```python
130
+ class PyHyperminhashCounter:
131
+ def __init__(self):
132
+ self.sk = pyhyperminhash.Sketch()
133
+
134
+ def step(self, *args) -> None:
135
+ self.sk += args
136
+
137
+ def finalize(self) -> int:
138
+ return int(self.sk)
139
+
140
+
141
+ con = sqlite3.connect(":memory:")
142
+ # Create the function for the current connection
143
+ con.create_aggregate("pyhmh", -1, PyHyperminhashCounter)
144
+
145
+ # Some dummy data
146
+ cur = con.execute("CREATE TABLE test(i, j)")
147
+ cur.executemany("INSERT INTO test(i, j) VALUES(?, ?)",
148
+ ((f'foo{i % 4291}bar', i % 819)
149
+ for i in range(1000000)))
150
+
151
+ # Returns exactly 502047
152
+ cur.execute("SELECT COUNT(*) FROM (SELECT DISTINCT i, j FROM test)")
153
+ print(cur.fetchone()[0])
154
+
155
+ # Returns approximately 500000, but faster
156
+ cur.execute("SELECT pyhmh(i, j) FROM test")
157
+ print(cur.fetchone()[0])
158
+ ```
159
+
160
+ ### Performance examples
161
+
162
+ Reading a file of 50 million lines, ~1,000,000 unique elements, 20 characters per line.
163
+
164
+ Method | Wall-clock time | Memory consumed | Count |
165
+ ---------------------------|-----------------|-----------------|------------|
166
+ Count lines | 2.3 seconds | _nil_ | 50,000,000 |
167
+ `set()` | 14.99 seconds | ~144 megabytes | 1,048,576 |
168
+ `Sketch.from_iter()` | 1.8 seconds | ~32 kilobytes | 1,041,936 |
169
+
170
+ For repeatable local benchmarks of the Python-facing API, use the `pyperf` suite:
171
+
172
+ ```bash
173
+ python3 -m venv .venv
174
+ source .venv/bin/activate
175
+ maturin develop -r
176
+ pip install pyperf
177
+ python3 examples/bench.py
178
+ ```
179
+
180
+ ### FAQ
181
+
182
+ * Can I extract an element that was previously added from a `Sketch`?
183
+ * No.
184
+ * Can I check if an element has previously been added to a `Sketch`?
185
+ * No.
186
+ * Wow, why use this, then?
187
+ * It's very fast at counting unique things. And it does not use much memory.
188
+
@@ -0,0 +1,9 @@
1
+ pyhyperminhash/__init__.py,sha256=bhBaICIKP_QxoP3VoISd10yFR3QQO5oFP5ChuNs3fgg,139
2
+ pyhyperminhash/__init__.pyi,sha256=7sIsBMiXTgseywi-qCoydl5YwvZGX-PbK735OP9LYQQ,2129
3
+ pyhyperminhash/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ pyhyperminhash/pyhyperminhash.pyd,sha256=00adBqDCjxv5SdQTbM0fg2rt-oL1_5SMsusNNTFZFn4,370688
5
+ pyhyperminhash-0.2.0.dist-info/METADATA,sha256=keCqhejg2ciHcUi6kC9egyq8B1QCWJHmuzqMxQJGbmU,6338
6
+ pyhyperminhash-0.2.0.dist-info/WHEEL,sha256=2muIeEoy5DUTfuM3RFMnd0bGlZCGVPOH8LjuzkFAm8s,95
7
+ pyhyperminhash-0.2.0.dist-info/licenses/LICENSE,sha256=5F9XyCGLCpCvSZi0l7noUGu1ISKges2jCstiLZhstdU,1106
8
+ pyhyperminhash-0.2.0.dist-info/sboms/pyhyperminhash.cyclonedx.json,sha256=qLvrRuQPbdG8qM3dqTAuxZVsA_rTAYIk55F_x_aerPc,71343
9
+ pyhyperminhash-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.13.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp39-abi3-win_arm64
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Lukas Lueg (lukas.lueg@gmail.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.