示例#1
0
 def tic(self, msg=None, **noteArgs):
     if msg is not None:
         if "methodName" not in noteArgs.keys():
             noteArgs["methodName"] = self._methodName
         if "end" not in noteArgs.keys(): noteArgs["end"] = ""
         _msg.note(msg=msg, **noteArgs)
     self.start = _time.time()
示例#2
0
 def note( self
         , msg = None
         , end = "\n"
         , pre = False
         ):
     self.timer.toc()
     if self._reportEnabled:
         if msg is None: msg = "done in " + str(np.round(self.timer.delta,decimals=6)) + " seconds."
         if pre:
             err.note( msg = msg
                     , methodName = self._methodName
                     , marginTop = 0
                     , marginBot = 0
                     , end = end
                     )
         else:
             print( msg, end = end )
     self.timer.toc()
示例#3
0
 def __init__( self
             , msg = None
             , methodName = ""
             , reportEnabled = True
             , end = "\n"
             ):
     self._methodName = methodName
     self._reportEnabled = reportEnabled
     self._oldFraction = 0.0
     self._clockCounter = None
     if self._reportEnabled and msg is not None:
         err.note( msg = msg
                 , methodName = self._methodName
                 , marginTop = 0
                 , marginBot = 0
                 , end = end
                 )
     self.timer = Timer()
示例#4
0
    def plot(self):
        """

        .. py:method:: plot(self)

        Generate a grid plot from the selected columns of the object's dataframe.

            **Parameters**

                None

            **Returns**

                None. However, this method causes side-effects by manipulating 
                the existing attributes of the object.

        """

        # setup figure

        scatterplotEnabled = self.scatterplot_kws is not None
        lineplotEnabled = self.lineplot_kws is not None
        distplotEnabled = self.distplot_kws is not None
        kdeplotEnabled = (self.kdeplot_kws is not None) and (self.kdecorner
                                                             is not None)
        lsplotEnabled = (scatterplotEnabled
                         or lineplotEnabled) and (self.lscorner is not None)

        if self.set_kws == (): self.set_kws = {}

        kdeUpperEnabled = False
        kdeLowerEnabled = False
        if kdeplotEnabled:
            if self.kdecorner == "auto":
                kdeLowerEnabled = True
            elif isinstance(self.kdecorner, str):
                if "upper" in self.kdecorner: kdeUpperEnabled = True
                if "lower" in self.kdecorner: kdeLowerEnabled = True
            else:
                raise Exception(
                    "The input argument 'kdecorner' must be either,\n\n" +
                    "    'upper', 'lower', 'upper-lower', 'auto',\n\n" +
                    "or otherwise None.")

        lsUpperEnabled = False
        lsLowerEnabled = False
        if lsplotEnabled:
            if self.lscorner == "auto":
                lsUpperEnabled = not kdeUpperEnabled
                lsLowerEnabled = not kdeLowerEnabled
                lsplotEnabled = lsLowerEnabled or lsUpperEnabled
            elif isinstance(self.lscorner, str):
                if "upper" in self.lscorner: lsUpperEnabled = True
                if "lower" in self.lscorner:
                    lsLowerEnabled = True
                    if self.kdecorner == "auto":
                        kdeUpperEnabled = not lsUpperEnabled
                        kdeLowerEnabled = not lsLowerEnabled
                        kdeplotEnabled = kdeLowerEnabled or kdeUpperEnabled
            else:
                raise Exception(
                    "The input argument 'lscorner' must be either,\n\n" +
                    "    'upper', 'lower', 'upper-lower', 'auto',\n\n" +
                    "or otherwise None.")
        elif kdeplotEnabled:
            if self.kdecorner == "auto":
                kdeUpperEnabled = True
                kdeLowerEnabled = True

        if self.kdeplot_kws == (): self.kdeplot_kws = {}
        if isinstance(self.kdeplot_kws, dict):
            #self.kdeplot_kws["cbar"]=True if lsplotEnabled==False else False
            self.kdeplot_kws["cbar"] = False
            if "cmap" not in self.kdeplot_kws.keys():
                self.kdeplot_kws["cmap"] = "Blues"
            if "alpha" not in self.kdeplot_kws.keys():
                self.kdeplot_kws["alpha"] = 1
            if "shade" not in self.kdeplot_kws.keys():
                self.kdeplot_kws["shade"] = True
            if "n_levels" not in self.kdeplot_kws.keys():
                self.kdeplot_kws["n_levels"] = 100
            if "shade_lowest" not in self.kdeplot_kws.keys():
                self.kdeplot_kws["shade_lowest"] = True
        elif kdeplotEnabled:
            raise Exception(
                "The input argument 'kdeplot_kws' must be None or a dictionary,\n"
                +
                "each (key,value) pair of which represents an (attribute,value)\n"
                + "of seaborn library's kdeplot() function.")

        if self.distplot_kws == (): self.distplot_kws = {}
        if isinstance(self.distplot_kws, dict):
            if "kde" not in self.distplot_kws.keys():
                self.distplot_kws["kde"] = False
            if "hist_kws" not in self.distplot_kws.keys():
                self.distplot_kws["hist_kws"] = {
                    "histtype": "stepfilled",
                    "linewidth": 1
                }
        elif distplotEnabled:
            raise Exception(
                "The input argument 'distplot_kws' must be None or a dictionary,\n"
                +
                "each (key,value) pair of which represents an (attribute,value)\n"
                + "of seaborn library's distplot() function.")

        if self.lineplot_kws == (): self.lineplot_kws = {}
        if isinstance(self.lineplot_kws, dict):
            self.lineplot_kws["figure_kws"] = None
            if "legend_kws" not in self.lineplot_kws.keys():
                self.lineplot_kws["legend_kws"] = None
            if "colorbar_kws" not in self.lineplot_kws.keys():
                self.lineplot_kws["colorbar_kws"] = None
            if "lc_kws" not in self.lineplot_kws.keys():
                self.lineplot_kws["lc_kws"] = {"cmap": "winter"}
            if "set_kws" not in self.lineplot_kws.keys():
                self.lineplot_kws["set_kws"] = self.set_kws
            if "ccolumns" not in self.lineplot_kws.keys():
                if scatterplotEnabled:
                    self.lineplot_kws["ccolumns"] = None
                else:
                    self.lineplot_kws["ccolumns"] = ()
            if self.lineplot_kws["ccolumns"] is None:
                if scatterplotEnabled:
                    if "plot_kws" in self.lineplot_kws.keys():
                        if "color" not in self.lineplot_kws["plot_kws"].keys():
                            self.lineplot_kws["plot_kws"]["color"] = "grey"
                        if "alpha" not in self.lineplot_kws["plot_kws"].keys():
                            self.lineplot_kws["plot_kws"]["alpha"] = 0.3
                    else:
                        self.lineplot_kws["plot_kws"] = {
                            "color": "grey",
                            "alpha": 0.3
                        }
            else:
                if "plot_kws" not in self.lineplot_kws.keys():
                    self.lineplot_kws["plot_kws"] = None
        elif lineplotEnabled:
            raise Exception(
                "The input argument 'lineplot_kws' must be None or a dictionary,\n"
                +
                "each (key,value) pair of which represents an (attribute,value)\n"
                + "of ParaMonte library's LinePlot() class.")

        if self.scatterplot_kws == (): self.scatterplot_kws = {}
        if isinstance(self.scatterplot_kws, dict):
            self.scatterplot_kws["figure_kws"] = None
            if "legend_kws" not in self.scatterplot_kws.keys():
                self.scatterplot_kws["legend_kws"] = None
            if "colorbar_kws" not in self.scatterplot_kws.keys():
                self.scatterplot_kws["colorbar_kws"] = None
            if "set_kws" not in self.scatterplot_kws.keys():
                self.scatterplot_kws["set_kws"] = self.set_kws
            if "scatter_kws" in self.scatterplot_kws.keys():
                if "s" not in self.scatterplot_kws["scatter_kws"].keys():
                    self.scatterplot_kws["scatter_kws"]["s"] = 3
                if "cmap" not in self.scatterplot_kws["scatter_kws"].keys():
                    self.scatterplot_kws["scatter_kws"]["cmap"] = "winter"
                if "alpha" not in self.scatterplot_kws["scatter_kws"].keys():
                    self.scatterplot_kws["scatter_kws"]["alpha"] = 1
                if "zorder" not in self.scatterplot_kws["scatter_kws"].keys():
                    self.scatterplot_kws["scatter_kws"]["zorder"] = 4
            else:
                self.scatterplot_kws["scatter_kws"] = {
                    "s": 3,
                    "cmap": "winter",
                    "alpha": 1,
                    "zorder": 4
                }
        elif scatterplotEnabled:
            raise Exception(
                "The input argument 'scatterplot_kws' must be None or a dictionary,\n"
                +
                "each (key,value) pair of which represents an (attribute,value)\n"
                + "of ParaMonte library's ScatterPlot() class.")

        if self.colorbar_kws == (): self.colorbar_kws = {}

        #########################
        if self._isdryrun: return
        #########################

        # check rows presence

        if self.rows is None:
            rowindex = range(len(self._dfref().index))
        else:
            rowindex = self.rows

        # check columns presence

        colnames, colindex = dfutils.getColNamesIndex(self._dfref().columns,
                                                      self.columns)

        # make pairgrid plot

        import seaborn as _sns
        from matplotlib import pyplot as _plt

        self.currentFig = ParaMonteFigure()
        if self.set_kws is not None: _sns.set(**self.set_kws)
        self.currentFig.pairgrid = _sns.PairGrid(
            self._dfref()[colnames].iloc[rowindex, :])
        self.currentFig.figure = self.currentFig.pairgrid.fig

        # hide axes as requested

        if (lsUpperEnabled or kdeUpperEnabled):
            self._upperEnabled = True
        else:
            self.hide("upper")
            self._upperEnabled = False

        if (lsLowerEnabled or kdeLowerEnabled):
            self._lowerEnabled = True
        else:
            self.hide("lower")
            self._lowerEnabled = False

        if distplotEnabled:
            self._diagEnabled = True
        else:
            self._diagEnabled = False
            self.hide("diag")

        # adjust subplot interspaces

        self.currentFig.pairgrid.fig.tight_layout()
        self.currentFig.pairgrid.fig.subplots_adjust(hspace=0.05, wspace=0.05)

        # set up figure layout if needed

        # check data type

        noDataPassed = ""
        if self._dfref is None:
            noDataPassed = "It appears that no data has been passed for plotting.\n"
        elif not isinstance(self._dfref, _wref.ref):
            noDataPassed = "It appears that you have messed with the\ninternal representation of data in the object.\n"
        elif not isinstance(self._dfref(), _pd.DataFrame):
            noDataPassed = "The input data is not a pandas' dataframe.\n"
        if noDataPassed:
            raise Exception(
                noDataPassed +
                "The input data must be a pandas' dataframe.\n" +
                "Please pass a dataFrame to the constructor or at\n" +
                "the time of calling the object (which is callable with\n" +
                "the same input arguments as the object's constructor.")

        #########################

        # define currentFig components

        nrow = len(self.currentFig.pairgrid.axes[:])
        nlsplotStr = str(
            (lsUpperEnabled + lsLowerEnabled) * nrow * (nrow - 1) // 2)
        nkdeplotStr = str(
            (kdeUpperEnabled + kdeLowerEnabled) * nrow * (nrow - 1) // 2)

        self.currentFig.targetList = [[[] for j in range(nrow)]
                                      for i in range(nrow)]
        #self.currentFig.kdeplotList     = [ [ [] for j in range(nrow) ] for i in range(nrow) ]
        self.currentFig.lineplotList = [[[] for j in range(nrow)]
                                        for i in range(nrow)]
        self.currentFig.scatterplotList = [[[] for j in range(nrow)]
                                           for i in range(nrow)]
        #self.currentFig.distplotList    = [ [] for i in range(nrow) ]

        #####################################
        #### add distplots as needed
        #####################################

        if distplotEnabled:
            self._timer.tic(
                msg="adding the diagonal histograms (distplots)... ", end=_eol)
            self.currentFig.pairgrid.map_diag(_sns.distplot,
                                              **self.distplot_kws)
            self._timer.toc()

#        if distplotEnabled:
#
#            msg.note( msg = "adding the diagonal histograms (distplots)...", methodName = self._methodName )
#
#            counter = 0
#            for irow, row in enumerate(self.currentFig.pairgrid.axes[:]):
#
#                counter += 1
#                self._timer.tic( msg = "    " + str(counter) + " out of " + nlsplotStr + ": " + colnames[irow] + " - " + colnames[irow] + "... ", end = _eol )
#
#                ax = row[irow]
#                _plt.sca(ax)
#
#                currentXlim = ax.get_xlim()
#                currentYlim = ax.get_ylim()
#
#                self.currentFig.distplotList[irow] = HistPlot   ( dataFrame = self._dfref()
#                                                                , columns = colnames[irow]
#                                                                , rows = self.rows
#                                                                , figure_kws = None
#                                                                , legend_kws = None
#                                                                , set_kws = self.set_kws
#                                                                , distplot_kws = self.distplot_kws
#                                                                )
#                self.currentFig.distplotList[irow].plot()
#
#                if self._lowerEnabled:
#                    ax.set_xlabel("")
#                    ax.set_ylabel("")
#
#                ax.set_xlim(currentXlim)
#                ax.set_ylim(currentYlim)
#
#                self._timer.toc()
#
#            _plt.tight_layout()

#####################################
#### add line/scatter plots as needed
#####################################

        if lsplotEnabled:

            msg.note(msg="adding the line/scatter plots...",
                     methodName=self._methodName)

            counter = 0
            for irow, row in enumerate(self.currentFig.pairgrid.axes[:]):

                for icol, ax in enumerate(row):

                    self.currentFig.targetList[irow][icol] = Target(axes=ax)

                    if (icol > irow
                            and lsUpperEnabled) or (icol < irow
                                                    and lsLowerEnabled):

                        counter += 1
                        self._timer.tic(msg="    " + str(counter) +
                                        " out of " + nlsplotStr + ": " +
                                        colnames[icol] + " - " +
                                        colnames[irow] + "... ",
                                        end=_eol)

                        _plt.sca(ax)
                        #_sns.set( self.set_kws )

                        if lineplotEnabled:

                            self.currentFig.lineplotList[irow][
                                icol] = LinePlot(dataFrame=self._dfref(),
                                                 xcolumns=colnames[icol],
                                                 ycolumns=colnames[irow],
                                                 rows=self.rows,
                                                 **self.lineplot_kws)
                            self.currentFig.lineplotList[irow][icol].plot()

                        if scatterplotEnabled:

                            self.currentFig.scatterplotList[irow][
                                icol] = ScatterPlot(dataFrame=self._dfref(),
                                                    xcolumns=colnames[icol],
                                                    ycolumns=colnames[irow],
                                                    rows=self.rows,
                                                    **self.scatterplot_kws)
                            self.currentFig.scatterplotList[irow][icol].plot()

                        #ax.get_xaxis().set_visible(False)
                        #ax.get_yaxis().set_visible(False)
                        #if irow < nrow-1: ax.set_xlabel("")
                        #if icol > 0: ax.set_ylabel("")
                        #ax.set_xlim(currentXlim)
                        #ax.set_ylim(currentYlim)

                        self._timer.toc()

            _plt.tight_layout()

        #####################################
        #### add kdeplots plots as needed
        #####################################

        if kdeplotEnabled:
            msg.note(
                msg=
                "adding kdeplots... depending on the number of plots, this may take a long while.",
                methodName=self._methodName)
            if kdeUpperEnabled:
                self._timer.tic(
                    msg="    adding the upper-triangle kdeplots...", end=_eol)
                self.currentFig.pairgrid.map_upper(_sns.kdeplot,
                                                   **self.kdeplot_kws)
                self._timer.toc()
            if kdeLowerEnabled:
                self._timer.tic(
                    msg="    adding the lower-triangle kdeplots...", end=_eol)
                self.currentFig.pairgrid.map_lower(_sns.kdeplot,
                                                   **self.kdeplot_kws)
                self._timer.toc()

        if self.outputFile is not None:
            self.currentFig.figure.savefig(self.outputFile,
                                           bbox_inches='tight',
                                           pad_inches=0.0)

        # set the ticks and labels

        nrowMinusOne = nrow - 1
        from matplotlib.pyplot import setp
        for irow, row in enumerate(self.currentFig.pairgrid.axes[:]):
            for icol, ax in enumerate(row):
                if self._lowerEnabled:
                    if icol > 0:
                        ax.set_ylabel("")
                    if irow < nrowMinusOne:
                        ax.set_xlabel("")
                elif self._upperEnabled:
                    if (not kdeplotEnabled):
                        if icol != irow:
                            ax.set_ylabel("")
                            ax.set_xlabel("")
                        if icol == irow:
                            ax.set_xlabel(colnames[irow])
                            ax.set_ylabel(colnames[icol])
                            ax.xaxis.set_tick_params(which="both",
                                                     labelbottom=True)
                            ax.yaxis.set_tick_params(which="both",
                                                     labelbottom=True)
                    elif icol > irow:
                        ax.set_ylabel("")
                        ax.set_xlabel("")

        import matplotlib.pyplot as _plt
        _plt.subplots_adjust(hspace=0.15, wspace=0.15)

        # add colorbar

        self._addcbar()
示例#5
0
def getFileList(file, fileType, methodName, _mpiDisabled):

    suffix = "_" + fileType + ".txt"
    if _os.path.isfile(
            file):  # check if the input path is a full path to a file
        FileList = [file]
        pattern = file
        if suffix != file[-len(suffix):]:
            _msg.warn(msg="The name of the input file: \n\n" + "    " + file +
                      "\n\n" + "does not end with the expected suffix '" +
                      suffix + "' for a " + fileType + " file type.\n",
                      methodName=methodName,
                      marginTop=1,
                      marginBot=1)
    elif _os.path.isdir(file):  # ensure the input path is not a directory
        _msg.abort(
            msg="file='" + file + "' cannot point to a directory.\n" +
            "Provide a string as the value of file that points to a unique " +
            fileType + " file or\n" +
            "to the unique name (including path) of the simulation name shared among its output files.\n",
            methodName=methodName,
            marginTop=1,
            marginBot=1)
    else:

        # search for files matching the input pattern

        import glob
        if file[-1:] == "*":
            pattern = file
        else:
            pattern = file + "*"  # + suffix

        _ = glob.glob(pattern)
        FileList = []
        for filename in _:
            if suffix in filename: FileList.append(filename)
        if len(FileList) == 0:
            _msg.abort(
                msg="Failed to detect any " + fileType +
                " files with the requested pattern: \n\n" + "    " + pattern +
                "\n\n" +
                "Provide a string, as the value of the input argument 'file', that either \n\n"
                + "    - points to one or more " + fileType +
                " files, or, \n" +
                "    - represents the unique name of a ParaMonte simulation. \n"
                +
                "      This unique-name is the common prefix in the names of \n"
                + "      the output files of a ParaMonte simulation.",
                methodName=methodName,
                marginTop=1,
                marginBot=1)
        else:
            pattern += suffix

    if _mpiDisabled:
        _msg.note(msg=str(len(FileList)) +
                  ' files detected matching the pattern: "' + pattern + '"',
                  methodName=methodName,
                  marginTop=0,
                  marginBot=0)
    return FileList
示例#6
0
def getFileList(file, fileSuffix, methodName, reportEnabled = True):

    FileList = []
    iswebfile = False
    fullSuffix = "_" + fileSuffix + ".txt"

    if os.path.isfile(file):

        # check if the input path is a full path to a file

        FileList.append(file)
        pattern = file
        if fullSuffix != file[-len(fullSuffix):]:
            err.warn( msg   = "The name of the input file: \n\n"
                            + "    " + file + "\n\n"
                            + "does not end with the expected suffix '" + fullSuffix + "' for a " + fileSuffix + " file type.\n"
                    , methodName = methodName
                    , marginTop = 1
                    , marginBot = 1
                    )

    else:

        import glob

        pattern = "" # not really needed, but just in case...
        for i in [1,2]:

            if i==1:

                #### first search for files matching the input pattern, then for directory

                pattern = file if "*" in file else file + "*" # + fullSuffix

            elif i==2:

                #### then search for file as directory

                if os.path.isdir(file):

                    pattern = os.path.join(file, "*" + fullSuffix)

                    if reportEnabled:
                        err.warn( msg   = "file='" + file + "' points to a directory.\n"
                                        + "Now searching inside the folder for a " + fileSuffix + " file..."
                                , methodName = methodName
                                , marginTop = 1
                                , marginBot = 1
                                )

            #### now search for pattern

            for filePath in glob.glob(pattern):
                if filePath.endswith(fullSuffix):
                    FileList.append(filePath)

            if len(FileList)>0: break

        if not pattern.endswith(fullSuffix): pattern += fullSuffix

        if len(FileList)==0:

            #### one last try, search the web

            try:
                import urllib.request
                #filePath = getRandomFilePrefix(prefix = methodName + "_" + fileSuffix)
                localFilePath, headers = urllib.request.urlretrieve(url = file)
                FileList.append(localFilePath)
                iswebfile = True
            except:
                err.abort   ( msg   = "Failed to detect any " + fileSuffix + " files with the requested pattern: \n\n"
                                    + "    " + pattern + "\n\n"
                                    + "Provide a string, as the value of the input argument ``file``, that either \n\n"
                                    + "    - points to one or more " + fileSuffix + " files, or, \n"
                                    + "    - represents the unique name of a ParaMonte simulation. \n"
                                    + "      This unique-name is the common prefix in the names of \n"
                                    + "      the output files of a ParaMonte simulation.\n\n"
                                    + "Most importantly, ensure the requested file is in ASCII format.\n"
                                    + "The binary-format chain or restart output files cannot be parsed.\n"
                                    + "You can request ASCII-format output files by setting the\n"
                                    + "appropriate simulation specifications of the " + methodName + " sampler,\n\n"
                                    + "    spec.restartFileFormat = \"ascii\"\n"
                                    + "    spec.chainFileFormat = \"ascii\""
                            , methodName = methodName
                            , marginTop = 1
                            , marginBot = 1
                            )

        elif reportEnabled:

            err.note( msg = str(len(FileList)) + ' files detected matching the pattern: "' + pattern + '"'
                    , methodName = methodName
                    , marginTop = 1
                    , marginBot = 1
                    )

    return FileList, iswebfile
示例#7
0
def getFileList(file, fileSuffix, methodName, reportEnabled):

    fullSuffix = "_" + fileSuffix + ".txt"

    if os.path.isfile(file):

        # check if the input path is a full path to a file

        FileList = [file]
        pattern = file
        if fullSuffix != file[-len(fullSuffix):]:
            err.warn( msg   = "The name of the input file: \n\n"
                            + "    " + file + "\n\n"
                            + "does not end with the expected suffix '" + fullSuffix + "' for a " + fileSuffix + " file type.\n"
                    , methodName = methodName
                    , marginTop = 1
                    , marginBot = 1
                    )

    else:

        if os.path.isdir(file):

            # ensure the input path is not a directory

            err.warn( msg   = "file='" + file + "' points to a directory.\n"
                            + "Now searching inside the folder for a " + fileSuffix + " file..."
                    , methodName = methodName
                    , marginTop = 1
                    , marginBot = 1
                    )
            pattern = os.path.join(file, "*" + fullSuffix)

        else:

            # search for files matching the input pattern

            if "*" in file: # file[-1:]=="*":
                pattern = file
            else:
                pattern = file + "*" # + fullSuffix

        import glob
        _ = glob.glob(pattern)

        FileList = []
        for filePath in _:
            if filePath.endswith(fullSuffix):
                FileList.append(filePath)

        if not pattern.endswith(fullSuffix): pattern += fullSuffix

        if len(FileList)==0:
            err.abort   ( msg   = "Failed to detect any " + fileSuffix + " files with the requested pattern: \n\n"
                                + "    " + pattern + "\n\n"
                                + "Provide a string, as the value of the input argument ``file``, that either \n\n"
                                + "    - points to one or more " + fileSuffix + " files, or, \n"
                                + "    - represents the unique name of a ParaMonte simulation. \n"
                                + "      This unique-name is the common prefix in the names of \n"
                                + "      the output files of a ParaMonte simulation.\n\n"
                                + "Most importantly, ensure the requested file is in ASCII format.\n"
                                + "The binary-format chain or restart output files cannot be parsed.\n"
                                + "You can request ASCII-format output files by setting the\n"
                                + "appropriate simulation specifications of the " + methodName + " sampler,\n\n"
                                + "    spec.restartFileFormat = \"ascii\"\n"
                                + "    spec.chainFileFormat = \"ascii\""
                        , methodName = methodName
                        , marginTop = 1
                        , marginBot = 1
                        )
        elif reportEnabled:
            err.note( msg = str(len(FileList)) + ' files detected matching the pattern: "' + pattern + '"'
                    , methodName = methodName
                    , marginTop = 1
                    , marginBot = 1
                    )

    return FileList