def basic_stats(
    G: nx.MultiDiGraph,
    *,
    area: float | None = None,
    clean_int_tol: float | None = None,
) -> dict[str, Any]:
    """
    Calculate basic descriptive geometric and topological measures of a graph.

    Density measures are only calculated if `area` is provided and clean
    intersection measures are only calculated if `clean_int_tol` is provided.

    Parameters
    ----------
    G
        Input graph.
    area
        If not None, calculate density measures and use `area` (in square
        meters) as the denominator.
    clean_int_tol
        If not None, calculate consolidated intersections count (and density,
        if `area` is also provided) and use this tolerance value. Refer to the
        `simplification.consolidate_intersections` function documentation for
        details.

    Returns
    -------
    stats
        Dictionary containing the following keys:
          - `circuity_avg` - see `circuity_avg` function documentation
          - `clean_intersection_count` - see `clean_intersection_count` function documentation
          - `clean_intersection_density_km` - `clean_intersection_count` per sq km
          - `edge_density_km` - `edge_length_total` per sq km
          - `edge_length_avg` - `edge_length_total / m`
          - `edge_length_total` - see `edge_length_total` function documentation
          - `intersection_count` - see `intersection_count` function documentation
          - `intersection_density_km` - `intersection_count` per sq km
          - `k_avg` - graph's average node degree (in-degree and out-degree)
          - `m` - count of edges in graph
          - `n` - count of nodes in graph
          - `node_density_km` - `n` per sq km
          - `self_loop_proportion` - see `self_loop_proportion` function documentation
          - `street_density_km` - `street_length_total` per sq km
          - `street_length_avg` - `street_length_total / street_segment_count`
          - `street_length_total` - see `street_length_total` function documentation
          - `street_segment_count` - see `street_segment_count` function documentation
          - `streets_per_node_avg` - see `streets_per_node_avg` function documentation
          - `streets_per_node_counts` - see `streets_per_node_counts` function documentation
          - `streets_per_node_proportions` - see `streets_per_node_proportions` function documentation
    """
    Gu = convert.to_undirected(G)
    stats: dict[str, Any] = {}

    stats["n"] = len(G.nodes)
    stats["m"] = len(G.edges)
    stats["k_avg"] = 2 * stats["m"] / stats["n"]
    stats["edge_length_total"] = edge_length_total(G)
    stats["edge_length_avg"] = stats["edge_length_total"] / stats["m"]
    stats["streets_per_node_avg"] = streets_per_node_avg(G)
    stats["streets_per_node_counts"] = streets_per_node_counts(G)
    stats["streets_per_node_proportions"] = streets_per_node_proportions(G)
    stats["intersection_count"] = intersection_count(G)
    stats["street_length_total"] = street_length_total(Gu)
    stats["street_segment_count"] = street_segment_count(Gu)
    stats["street_length_avg"] = stats["street_length_total"] / stats["street_segment_count"]
    stats["circuity_avg"] = circuity_avg(Gu)
    stats["self_loop_proportion"] = self_loop_proportion(Gu)

    # calculate clean intersection counts if requested
    if clean_int_tol:
        stats["clean_intersection_count"] = len(
            simplification.consolidate_intersections(
                G,
                tolerance=clean_int_tol,
                rebuild_graph=False,
                dead_ends=False,
            ),
        )

    # can only calculate density measures if area was provided
    if area is not None:
        area_km = area / 1_000_000  # convert m^2 to km^2
        stats["node_density_km"] = stats["n"] / area_km
        stats["intersection_density_km"] = stats["intersection_count"] / area_km
        stats["edge_density_km"] = stats["edge_length_total"] / area_km
        stats["street_density_km"] = stats["street_length_total"] / area_km
        if clean_int_tol:
            stats["clean_intersection_density_km"] = stats["clean_intersection_count"] / area_km

    return stats

    Calculate basic descriptive geometric and topological measures of a graph.

    Density measures are only calculated if `area` is provided and clean
    intersection measures are only calculated if `clean_int_tol` is provided.

    Parameters
    ----------
    G
        Input graph.
    area
        If not None, calculate density measures and use `area` (in square
        meters) as the denominator.
    clean_int_tol
        If not None, calculate consolidated intersections count (and density,
        if `area` is also provided) and use this tolerance value. Refer to the
        `simplification.consolidate_intersections` function documentation for
        details.

    Returns
    -------
    stats
        Dictionary containing the following keys:
          - `circuity_avg` - see `circuity_avg` function documentation
          - `clean_intersection_count` - see `clean_intersection_count` function documentation
          - `clean_intersection_density_km` - `clean_intersection_count` per sq km
          - `edge_density_km` - `edge_length_total` per sq km
          - `edge_length_avg` - `edge_length_total / m`
          - `edge_length_total` - see `edge_length_total` function documentation
          - `intersection_count` - see `intersection_count` function documentation
          - `intersection_density_km` - `intersection_count` per sq km
          - `k_avg` - graph's average node degree (in-degree and out-degree)
          - `m` - count of edges in graph
          - `n` - count of nodes in graph
          - `node_density_km` - `n` per sq km
          - `self_loop_proportion` - see `self_loop_proportion` function documentation
          - `street_density_km` - `street_length_total` per sq km
          - `street_length_avg` - `street_length_total / street_segment_count`
          - `street_length_total` - see `street_length_total` function documentation
          - `street_segment_count` - see `street_segment_count` function documentation
          - `streets_per_node_avg` - see `streets_per_node_avg` function documentation
          - `streets_per_node_counts` - see `streets_per_node_counts` function documentation
          - `streets_per_node_proportions` - see `streets_per_node_proportions` function documentation
    
osmnx/stats.py