def pandas_fillna(df, by, hasna=None, suffix=None):
    """
    Replaces the :epkg:`nan` values for something not :epkg:`nan`.
    Mostly used by @see fn pandas_groupby_nan.

    @param      df      dataframe
    @param      by      list of columns for which we need to replace nan
    @param      hasna   None or list of columns for which we need to replace NaN
    @param      suffix  use a prefix for the NaN value
    @return             list of values chosen for each column, new dataframe (new copy)
    """
    suffix = suffix if suffix else '²'
    df = df.copy()
    rep = {}
    for c in by:
        if hasna is not None and c not in hasna:
            continue
        if df[c].dtype in (str, bytes, object):
            se = set(df[c].dropna())
            val = se.pop()
            if isinstance(val, str):
                cst = suffix
                val = ''
            elif isinstance(val, bytes):
                cst = b'_'
            else:
                raise TypeError(
                    "Unable to determine a constant for type='{0}' dtype='{1}'"
                    .format(val, df[c].dtype))
            val += cst
            while val in se:
                val += suffix
            df[c].fillna(val, inplace=True)
            rep[c] = val
        else:
            dr = df[c].dropna()
            mi = abs(dr.min())
            ma = abs(dr.max())
            val = ma + mi
            if val <= ma:
                raise ValueError(
                    "Unable to find a different value for column '{0}': min={1} max={2}"
                    .format(val, mi, ma))
            df[c].fillna(val, inplace=True)
            rep[c] = val
    return rep, df
