This plugin provides stable hashing functionality across different polars versions.
π Documentation β every expression, its input and output types, and its arguments.
import polars as pl
import polars_hash as plh
df = pl.DataFrame({
"foo":["hello_world"]
})
result = df.select(plh.col('foo').chash.sha256())
print(result)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β foo β
β --- β
β str β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ‘
β 35072c1ae546350e0bfa7ab11d49dc6f129e72ccd57ec7eb671225bbd197c8f1 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββdf = pl.DataFrame({
"foo":["hello_world"]
})
result = df.select(plh.col('foo').nchash.wyhash())
print(result)
ββββββββββββββββββββββββ
β foo β
β --- β
β u64 β
ββββββββββββββββββββββββ‘
β 16737367591072095403 β
ββββββββββββββββββββββββ
result = df.select(plh.col('foo').nchash.farmhash64())
print(result)
ββββββββββββββββββββββββ
β foo β
β --- β
β u64 β
ββββββββββββββββββββββββ‘
β 15605398435621216523 β
ββββββββββββββββββββββββ
result = df.select(plh.col('foo').nchash.farmhash32())
print(result)
ββββββββββββββ
β foo β
β --- β
β u32 β
ββββββββββββββ‘
β 1719156559 β
ββββββββββββββ
result = df.select(plh.col('foo').nchash.cityhash128())
print(result)
βββββββββββββββββββββββββββββββββββββββββββ
β foo β
β --- β
β u128 β
βββββββββββββββββββββββββββββββββββββββββββ‘
β 133423608296839006301901834072762183026 β
βββββββββββββββββββββββββββββββββββββββββββ
result = df.select(plh.col('foo').nchash.gxhash64())
print(result)
βββββββββββββββββββββββ
β foo β
β --- β
β u64 β
βββββββββββββββββββββββ‘
β 2180020304351407825 β
βββββββββββββββββββββββcityhash32() and cityhash64() return the values printed above for farmhash32()
and farmhash64(). That is expected: FarmHash reuses CityHash for short input, and
hello_world is 11 bytes. See
the CityHash reference.
The GxHash expressions need a CPU with AES instructions and have no software fallback.
Every x86, x86-64 and aarch64 wheel is built for them; there are no linux-armv7 or
linux-ppc64le wheels from 0.8.0 on, because GxHash cannot be built for either. See
the GxHash reference.
df = pl.DataFrame(
{"coord": [{"longitude": -120.6623, "latitude": 35.3003}]},
schema={
"coord": pl.Struct(
[pl.Field("longitude", pl.Float64), pl.Field("latitude", pl.Float64)]
),
},
)
df.with_columns(
plh.col('coord').geohash.from_coords().alias('geohash')
)
shape: (1, 2)
βββββββββββββββββββββββ¬βββββββββββββββ
β coord β geohash β
β --- β --- β
β struct[2] β str β
βββββββββββββββββββββββͺβββββββββββββββ‘
β {-120.6623,35.3003} β 9q60y60rhsgg β
βββββββββββββββββββββββ΄βββββββββββββββ
pl.select(pl.lit('9q60y60rhs').geohash.to_coords().alias('coordinates'))
shape: (1, 1)
βββββββββββββββββββββββββ
β coordinates β
β --- β
β struct[2] β
βββββββββββββββββββββββββ‘
β {-120.6623,35.300298} β
βββββββββββββββββββββββββdf = pl.DataFrame(
{"coord": [{"longitude": -120.6623, "latitude": 35.3003}]},
schema={
"coord": pl.Struct(
[pl.Field("longitude", pl.Float64), pl.Field("latitude", pl.Float64)]
),
},
)
df.with_columns(
plh.col('coord').h3.from_coords().alias('h3')
)
shape: (1, 2)
βββββββββββββββββββββββ¬ββββββββββββββββββ
β coord β h3 β
β --- β --- β
β struct[2] β str β
βββββββββββββββββββββββͺββββββββββββββββββ‘
β {-120.6623,35.3003} β 8c29adc423821ff β
βββββββββββββββββββββββ΄ββββββββββββββββββBins timestamps into variable-precision sliding windows of time, so rows that fall in the same window share a hash. Timestamps must lie between 1970-01-01 and 2098-01-01. A higher precision means a shorter window: 10 covers about 4 seconds, 8 about 4 minutes.
Precision may be 1 to 32, but past about 18 the hash stops changing for present-day timestamps and the extra characters are padding. The exact point depends on the date: timestamps close to 1970 keep splitting to about 21, far-future ones run out sooner.
from datetime import datetime
df = pl.DataFrame({"datetime": [datetime(2017, 2, 21, 20, 15, 13)]})
df.with_columns(
plh.col('datetime').timehash.from_datetime().alias('timehash')
)
shape: (1, 2)
βββββββββββββββββββββββ¬βββββββββββββ
β datetime β timehash β
β --- β --- β
β datetime[ΞΌs] β str β
βββββββββββββββββββββββͺβββββββββββββ‘
β 2017-02-21 20:15:13 β afcccc0e1b β
βββββββββββββββββββββββ΄βββββββββββββ
pl.select(pl.lit('afcccc0e1b').timehash.to_datetime().alias('datetime'))
shape: (1, 1)
ββββββββββββββββββββββββββββββββββ
β datetime β
β --- β
β datetime[ΞΌs, UTC] β
ββββββββββββββββββββββββββββββββββ‘
β 2017-02-21 20:15:11.292315 UTC β
ββββββββββββββββββββββββββββββββββ
pl.select(pl.lit('afcccc0e1b').timehash.neighbors().alias('neighbors'))
shape: (1, 1)
βββββββββββββββββββββββββββββββ
β neighbors β
β --- β
β struct[2] β
βββββββββββββββββββββββββββββββ‘
β {"afcccc0e1a","afcccc0e1c"} β
βββββββββββββββββββββββββββββββdf = pl.DataFrame({"foo": ["hello_world"], "bar": ["today"]})
result = df.select(plh.concat_str("foo", "bar").chash.sha256())hash_rows gives each row bytes that no other row can make, for all column types.
Any hasher then reads those bytes.
df = pl.DataFrame(
{"foo": ["hello_world"], "bar": [42], "baz": [[1, 2, 3]], "qux": [{"a": 1}]}
)
df.select(plh.hash_rows(pl.all()).chash.sha2_256())
shape: (1, 1)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β foo β
β --- β
β str β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ‘
β 9055866af8d3c113e0a8fdb729ce8e6fa67ed5f6f51efa8235a588e88ea972f4 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββThe encoder reads the meaning of a value, not the polars storage of it. An Int32 and
the Int64 next to it make the same hash. A Datetime in milliseconds and the same
time in nanoseconds also make the same hash, and a Categorical makes the hash of its
string. The encoder does not read the column names. Therefore a new name keeps the
hash, but a new order does not. The
reference
gives all the rules and the byte layout of version 1, which does not change.