Skip to content

Instantly share code, notes, and snippets.

@isentropic
Last active January 7, 2022 13:50
Show Gist options
  • Save isentropic/a86effab2c007e86912a50f995cac52b to your computer and use it in GitHub Desktop.
Save isentropic/a86effab2c007e86912a50f995cac52b to your computer and use it in GitHub Desktop.
Tensorflow histogram2d (Simple implementation)
import tensorflow as tf
@tf.function
def get2dHistogram(x, y,
value_range,
nbins=100,
dtype=tf.dtypes.int32):
"""
Bins x, y coordinates of points onto simple square 2d histogram
Given the tensor x and y:
x: x coordinates of points
y: y coordinates of points
this operation returns a rank 2 `Tensor`
representing the indices of a histogram into which each element
of `values` would be binned. The bins are equal width and
determined by the arguments `value_range` and `nbins`.
Args:
x: Numeric `Tensor`.
y: Numeric `Tensor`.
value_range[0] lims for x
value_range[1] lims for y
nbins: Scalar `int32 Tensor`. Number of histogram bins.
dtype: dtype for returned histogram.
Example:
N = 1000
xs = tf.random.normal([N])
ys = tf.random.normal([N])
get2dHistogram(xs, ys, ([-5.0, 5.0], [-5.0, 5.0]), 50)
"""
x_range = value_range[0]
y_range = value_range[1]
histy_bins = tf.histogram_fixed_width_bins(y, y_range, nbins=nbins, dtype=dtype)
H = tf.map_fn(lambda i: tf.histogram_fixed_width(x[histy_bins == i], x_range, nbins=nbins),
tf.range(nbins))
return H # Matrix!
@isentropic
Copy link
Author

isentropic commented May 5, 2020

My TF version is '2.2.0-rc4' with 3.8 python, but this is not the cause, this snippet worked with 2.0 TF

@CatherineTaelman
Copy link

Thank you for the example and extra information! I downgraded TF from 2.1 to 2.0 and now it works..

@isentropic
Copy link
Author

I think TF version is irrelevant here, it works with 2.2 as well

@LucasKirsten
Copy link

How could I change the code to add a weight argument, such as in numpy implementation?

I have tried so far the following, but it does not return the correct values for the histogram:
`def get2dHistogram(x, y, weights,
value_range,
nbins=100,
dtype=tf.dtypes.int32):

x_range = value_range[0]
y_range = value_range[1]

x = tf.histogram_fixed_width_bins(x, y_range, nbins=tf.size(x), dtype=dtype)
x = tf.math.bincount(x, weights=weights, minlength=tf.size(y))
y = tf.histogram_fixed_width_bins(y, y_range, nbins=tf.size(y), dtype=dtype)
y = tf.math.bincount(y, weights=weights, minlength=tf.size(y))

histy_bins = tf.histogram_fixed_width_bins(y, y_range, nbins=nbins, dtype=dtype)

H = tf.map_fn(lambda i: tf.histogram_fixed_width(x[histy_bins == i], x_range, nbins=nbins), tf.range(nbins))
return H # Matrix!`

@isentropic
Copy link
Author

isentropic commented Jan 7, 2022

maybe you could multiply the resulting histogram with weights
...
edit: this wont work I get it.
Perhaps if weights aren't that important perhaps duplicate (add multiplicity) to some of the points manually?

@LucasKirsten
Copy link

LucasKirsten commented Jan 7, 2022

I managed to make it work using the tensorflow-probability package for the histogram implementation. The final is below:

`def get2dHistogram(x, y, weights,
value_range,
nbins=100,
dtype=tf.dtypes.int32):

x_range = tf.linspace(value_range[0][0], value_range[0][1], num=nbins+1)
x = tf.clip_by_value(x, value_range[0][0], value_range[0][1])

histy_bins = tf.histogram_fixed_width_bins(y, value_range[1], nbins=nbins, dtype=dtype)

hists = []
for i in range(nbins):
_x = x[histy_bins == i]
_w = weights[histy_bins == i]
hist = tfp.stats.histogram(_x, edges=x_range, weights=_w)
hists.append(hist)

return tf.stack(hists, axis=0)`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment