import pandas as pd
import numpy as np
from src.si.util.util import label_gen

__all__ = ['Dataset']

class Dataset:
    def __init__(self, X=None, Y=None,
                 xnames: list = None,
                 yname: str = None):
        """ Tabular Dataset"""
        if X is None:
            raise Exception("Trying to instanciate a DataSet without any data")
        self.X = X
        self.Y = Y
        self.xnames = xnames if xnames else label_gen(X.shape[1])
        self.yname = yname if yname else 'Y'

    @classmethod
    def from_data(cls, filename, sep=",", labeled=True):
        """Creates a DataSet from a data file.

        :param filename: The filename
        :type filename: str
        :param sep: attributes separator, defaults to ","
        :type sep: str, optional
        :return: A DataSet object
        :rtype: DataSet
        """
        data = np.genfromtxt(filename, delimiter=sep)
        if labeled:
            X = data[:, 0:-1]
            Y = data[:, -1]
        else:
            X = data
            Y = None
        return cls(X, Y)

    @classmethod
    def from_dataframe(cls, df, ylabel=None):
        """Creates a DataSet from a pandas dataframe.
        :param df: [description]
        :type df: [type]
        :param ylabel: [description], defaults to None
        :type ylabel: [type], optional
        :return: [description]
        :rtype: [type]
        """

        if ylabel and ylabel in df.columns:
            X = df.loc[:, df.columns != ylabel].to_numpy() #transforma num array de numpy
            Y = df.loc[:, ylabel].to_numpy()
            # xnames = df.columns.tolist().remove(ylabel)
            yname = ylabel
            xnames = df.columns.tolist()
            for name in xnames:
                if name == yname:
                    xnames.remove(yname)

        else:
            X = df.to_numpy()
            Y = None
            xnames = df.columns.tolist()
            yname = None

        return cls(X, Y, xnames, yname)


    def __len__(self):
        """Returns the number of data points."""
        return self.X.shape[0]

    def hasLabel(self):
        """Returns True if the dataset constains labels (a dependent variable)"""
        return self.Y is not None

    def getNumFeatures(self):
        """Returns the number of features"""
        return self.X.shape[1]

    def getNumClasses(self):
        """Returns the number of label classes or 0 if the dataset has no dependent variable."""
        return len(np.unique(self.Y)) if self.hasLabel() else 0

    def writeDataset(self, filename, sep=","):
        """Saves the dataset to a file

        :param filename: The output file path
        :type filename: str
        :param sep: The fields separator, defaults to ","
        :type sep: str, optional
        """

        fullds = np.hstack((self.X, self.Y.reshape(len(self.Y), 1)))
        np.savetxt(filename, fullds, delimiter=sep)

    def toDataframe(self):
        """ Converts the dataset into a pandas DataFrame"""
        if self.hasLabel():
            df = pd.DataFrame(np.hstack((self.X, self.Y.reshape(len(self.Y), 1))), columns=self.xnames[:]+[self.yname]) #columns=np.hstack((self.xnames, self.yname)))
        else:
            df = pd.DataFrame(self.X.copy(), columns=self.xnames[:])
        return df

    def getXy(self):
        return self.X, self.Y


def summary(dataset, format='df'):
    """ Returns the statistics of a dataset(mean, std, max, min)

    :param dataset: A Dataset object
    :type dataset: si.data.Dataset
    :param format: Output format ('df':DataFrame, 'dict':dictionary ), defaults to 'df'
    :type format: str, optional
    """
    if format not in ["df", "dict"]:
        raise Exception("Invalid format. Choose between 'df' and 'dict'.")
    if dataset.hasLabel():
        data = np.hstack((dataset.X, dataset.Y.reshape(len(dataset.Y), 1)))
        #data = np.hstack([dataset.X, np.reshape(dataset.Y, (-1, 1))])
        columns = dataset.xnames[:] + [dataset.yname]
    else:
        data = dataset.X
        columns = dataset.xnames[:]
    stats = {}
    if type(dataset.Y[0]) is str:
        for i in range(data.shape[1]-1): #ve colunas
            _means = np.mean(data[:, i], axis=0)
            _vars = np.var(data[:, i], axis=0)
            _maxs = np.max(data[:, i], axis=0)
            _mins = np.min(data[:, i], axis=0)

            stat = {"mean": _means,
                    "var": _vars,
                    "max": _maxs,
                    "min": _mins
                    }
            stats[columns[i]] = stat
    else:
        for i in range(data.shape[1]):  # ve colunas
            _means = np.mean(data[:, i], axis=0)
            _vars = np.var(data[:, i], axis=0)
            _maxs = np.max(data[:, i], axis=0)
            _mins = np.min(data[:, i], axis=0)

            stat = {"mean": _means,
                    "var": _vars,
                    "max": _maxs,
                    "min": _mins
                    }
            stats[columns[i]] = stat
    # _means = np.mean(data, axis=0)
    # _vars = np.var(data, axis=0)
    # _maxs = np.max(data, axis=0)
    # _mins = np.min(data, axis=0)
    # stats = {}
    # for i in range(data.shape[1]):
    #     stat = {"mean": _means[i],
    #             "var": _vars[i],
    #             "max": _maxs[i],
    #             "min": _mins[i]
    #             }
    #     stats[columns[i]] = stat

    if format == "dict":
        return stats
    else:
        return pd.DataFrame(stats)

[((30, 15, 30, 53), 'numpy.genfromtxt', 'np.genfromtxt', (), '', True, 'import numpy as np\n'), ((95, 8, 95, 51), 'numpy.savetxt', 'np.savetxt', (), '', True, 'import numpy as np\n'), ((169, 15, 169, 34), 'pandas.DataFrame', 'pd.DataFrame', ({(169, 28, 169, 33): 'stats'}, {}), '(stats)', True, 'import pandas as pd\n'), ((16, 44, 16, 65), 'src.si.util.util.label_gen', 'label_gen', ({(16, 54, 16, 64): 'X.shape[1]'}, {}), '(X.shape[1])', False, 'from src.si.util.util import label_gen\n'), ((129, 21, 129, 48), 'numpy.mean', 'np.mean', (), '', True, 'import numpy as np\n'), ((130, 20, 130, 46), 'numpy.var', 'np.var', (), '', True, 'import numpy as np\n'), ((131, 20, 131, 46), 'numpy.max', 'np.max', (), '', True, 'import numpy as np\n'), ((132, 20, 132, 46), 'numpy.min', 'np.min', (), '', True, 'import numpy as np\n'), ((142, 21, 142, 48), 'numpy.mean', 'np.mean', (), '', True, 'import numpy as np\n'), ((143, 20, 143, 46), 'numpy.var', 'np.var', (), '', True, 'import numpy as np\n'), ((144, 20, 144, 46), 'numpy.max', 'np.max', (), '', True, 'import numpy as np\n'), ((145, 20, 145, 46), 'numpy.min', 'np.min', (), '', True, 'import numpy as np\n'), ((83, 19, 83, 36), 'numpy.unique', 'np.unique', ({(83, 29, 83, 35): 'self.Y'}, {}), '(self.Y)', True, 'import numpy as np\n')]