code: def _generate_hist2d_data(x, y, bins, range):
      source = x._indices.source
      y_source = y._indices.source
      if source is None or y_source is None:
          raise ValueError(
              "histogram_2d expects two expressions of 'Table', found scalar expression"
          )
      if isinstance(source, hail.MatrixTable):
          raise ValueError(
              "histogram_2d requires source to be Table, not MatrixTable"
          )
      if source != y_source:
          raise ValueError(
              f"histogram_2d expects two expressions from the same 'Table', found {source} and {y_source}"
          )
      raise_unless_row_indexed("histogram_2d", x)
      raise_unless_row_indexed("histogram_2d", y)
      if isinstance(bins, int):
          x_bins = y_bins = bins
      else:
          x_bins, y_bins = bins
      if range is None:
          x_range = y_range = None
      else:
          x_range, y_range = range
      if x_range is None or y_range is None:
          warning(
              "At least one range was not defined in histogram_2d. Doing two passes..."
          )
          ranges = source.aggregate(
              hail.struct(
                  x_stats=hail.agg.stats(x), y_stats=hail.agg.stats(y)
              )
          )
          if x_range is None:
              x_range = (ranges.x_stats.min, ranges.x_stats.max)
          if y_range is None:
              y_range = (ranges.y_stats.min, ranges.y_stats.max)
      else:
          warning(
              "If x_range or y_range are specified in histogram_2d, and there are points "
              "outside of these ranges, they will not be plotted"
          )
      x_range = list(map(float, x_range))
      y_range = list(map(float, y_range))
      x_spacing = (x_range[1] - x_range[0]) / x_bins
      y_spacing = (y_range[1] - y_range[0]) / y_bins
  
      def frange(start, stop, step):
          from itertools import count, takewhile
  
          return takewhile(lambda x: x <= stop, count(start, step))
  
      x_levels = hail.literal(
          list(frange(x_range[0], x_range[1], x_spacing))[::-1]
      )
      y_levels = hail.literal(
          list(frange(y_range[0], y_range[1], y_spacing))[::-1]
      )
      grouped_ht = source.group_by(
          x=hail.str(x_levels.find(lambda w: x >= w)),
          y=hail.str(y_levels.find(lambda w: y >= w)),
      ).aggregate(c=hail.agg.count())
      data = grouped_ht.filter(
          hail.is_defined(grouped_ht.x)
          & (grouped_ht.x != str(x_range[1]))
          & hail.is_defined(grouped_ht.y)
          & (grouped_ht.y != str(y_range[1]))
      )
      return data
  
imports: import math
  import collections
  import numpy
  import pandas
  import bokeh
  import bokeh.io
  import bokeh.models
  import warnings
  from bokeh.models import HoverTool, ColorBar, LogTicker, LogColorMapper, LinearColorMapper, CategoricalColorMapper, ColumnDataSource, BasicTicker, Plot, CDSView, GroupFilter, IntersectionFilter, Legend, LegendItem, Renderer, CustomJS, Select, Column, Span, DataRange1d, Slope, Label, ColorMapper, GridPlot
  import bokeh.plotting
  import bokeh.palettes
  from bokeh.plotting import figure
  from bokeh.transform import transform
  from bokeh.layouts import gridplot
  from hail.expr import aggregators
  from hail.expr.expressions import Expression, NumericExpression, StringExpression, LocusExpression, Int32Expression, Int64Expression, Float32Expression, Float64Expression, expr_numeric, expr_float64, expr_any, expr_locus, expr_str, raise_unless_row_indexed
  from hail.expr.functions import _error_from_cdf_python
  from hail.typecheck import typecheck, oneof, nullable, sized_tupleof, numeric, sequenceof, dictof
  from hail import Table, MatrixTable
  from hail.utils.struct import Struct
  from hail.utils.java import warning
  from typing import List, Tuple, Dict, Union, Callable, Optional, Sequence, Any, Set
  import hail
label_desc: Use image node_id 11 for calling a global function with example usage: _generate_hist2d_data(x, y, bins, range) and returns: data, takewhile