from typing import Counter
import math


def conditional_entropy(x_symbols, y_symbols):
    """
    Computes the entropy of `x` given `y`.

    :param list x_symbols: A list of all observed `x` symbols.
    :param list y_symbols: A list of all observed `y` symbols.

    :return float entropy: The conditional entropy of `x` given `y`.
    """

    # Cache counters; while the xy_counter is already computed in other
    # parts, particularly in the scorers, it is worth repeating the code
    # here to have a more general function.
    y_counter = Counter(y_symbols)
    xy_counter = Counter(list(zip(x_symbols, y_symbols)))
    population = sum(y_counter.values())

    # Compute the entropy and return
    entropy = 0
    for xy_pair, xy_count in xy_counter.items():
        p_xy = xy_count / population
        p_y = y_counter[xy_pair[1]] / population
        entropy += p_xy * math.log(p_y / p_xy)

    return entropy