Create a confusion matrix
    
    In each axis of the resulting confusion matrix the negative case is
    0-index and the positive case 1-index. The labels get sorted, in a
    True/False scenario true positives will occur at (1,1). The first dimension
    (rows) of the resulting matrix is the expected class and the second
    dimension (columns) is the observed class.
    
    :param exp: expected values
    :type exp: list of float
    :param obs: observed values
    :type obs: list of float
    :rtype: tuple of square matrix and sorted labels

def confusion_matrix(exp, obs):
    """Create a confusion matrix
    
    In each axis of the resulting confusion matrix the negative case is
    0-index and the positive case 1-index. The labels get sorted, in a
    True/False scenario true positives will occur at (1,1). The first dimension
    (rows) of the resulting matrix is the expected class and the second
    dimension (columns) is the observed class.
    
    :param exp: expected values
    :type exp: list of float
    :param obs: observed values
    :type obs: list of float
    :rtype: tuple of square matrix and sorted labels
    """
    assert len(exp) == len(obs)
    # Expected in the first dimension (0;rows), observed in the second (1;cols)
    lbls = sorted(set(exp))
    res = numpy.zeros(shape=(len(lbls), len(lbls)))
    for i in range(len(exp)):
        res[lbls.index(exp[i]), lbls.index(obs[i])] += 1
    return res, lbls