Queries the dataset to retrieve the contained data.

        Args:
            attributes (list[str]): Names of attributes to fetch in query.
                Attribute names must correspond to valid attributes. See
                the attributes property for a list of valid attributes.
            filters (dict[str,any]): Dictionary of filters --> values
                to filter the dataset by. Filter names and values must
                correspond to valid filters and filter values. See the
                filters property for a list of valid filters.
            only_unique (bool): Whether to return only rows containing
                unique values (True) or to include duplicate rows (False).
            use_attr_names (bool): Whether to use the attribute names
                as column names in the result (True) or the attribute
                display names (False).
            dtypes (dict[str,any]): Dictionary of attributes --> data types
                to describe to pandas how the columns should be handled

        Returns:
            pandas.DataFrame: DataFrame containing the query results.

def query(self,
              attributes=None,
              filters=None,
              only_unique=True,
              use_attr_names=False,
              dtypes = None
              ):
        """Queries the dataset to retrieve the contained data.

        Args:
            attributes (list[str]): Names of attributes to fetch in query.
                Attribute names must correspond to valid attributes. See
                the attributes property for a list of valid attributes.
            filters (dict[str,any]): Dictionary of filters --> values
                to filter the dataset by. Filter names and values must
                correspond to valid filters and filter values. See the
                filters property for a list of valid filters.
            only_unique (bool): Whether to return only rows containing
                unique values (True) or to include duplicate rows (False).
            use_attr_names (bool): Whether to use the attribute names
                as column names in the result (True) or the attribute
                display names (False).
            dtypes (dict[str,any]): Dictionary of attributes --> data types
                to describe to pandas how the columns should be handled

        Returns:
            pandas.DataFrame: DataFrame containing the query results.

        """

        # Example query from Ensembl biomart:
        #
        # <?xml version="1.0" encoding="UTF-8"?>
        # <!DOCTYPE Query>
        # <Query  virtualSchemaName = "default" formatter = "TSV" header = "0"
        #  uniqueRows = "0" count = "" datasetConfigVersion = "0.6" >
        #   <Dataset name = "hsapiens_gene_ensembl" interface = "default" >
        #       <Filter name = "chromosome_name" value = "1,2"/>
        #       <Filter name = "end" value = "10000000"/>
        #       <Filter name = "start" value = "1"/>
        #       <Attribute name = "ensembl_gene_id" />
        #       <Attribute name = "ensembl_transcript_id" />
        #   </Dataset>
        # </Query>

        # Setup query element.
        root = ElementTree.Element('Query')
        root.set('virtualSchemaName', self._virtual_schema)
        root.set('formatter', 'TSV')
        root.set('header', '1')
        root.set('uniqueRows', native_str(int(only_unique)))
        root.set('datasetConfigVersion', '0.6')

        # Add dataset element.
        dataset = ElementTree.SubElement(root, 'Dataset')
        dataset.set('name', self.name)
        dataset.set('interface', 'default')

        # Default to default attributes if none requested.
        if attributes is None:
            attributes = list(self.default_attributes.keys())

        # Add attribute elements.
        for name in attributes:
            try:
                attr = self.attributes[name]
                self._add_attr_node(dataset, attr)
            except KeyError:
                raise BiomartException(
                    'Unknown attribute {}, check dataset attributes '
                    'for a list of valid attributes.'.format(name))

        if filters is not None:
            # Add filter elements.
            for name, value in filters.items():
                try:
                    filter_ = self.filters[name]
                    self._add_filter_node(dataset, filter_, value)
                except KeyError:
                    raise BiomartException(
                        'Unknown filter {}, check dataset filters '
                        'for a list of valid filters.'.format(name))

        # Fetch response.
        response = self.get(query=ElementTree.tostring(root))

        # Raise exception if an error occurred.
        if 'Query ERROR' in response.text:
            raise BiomartException(response.text)

        # Parse results into a DataFrame.
        try:
            result = pd.read_csv(StringIO(response.text), sep='\t', dtype=dtypes)
        # Type error is raised of a data type is not understood by pandas
        except TypeError as err:
            raise ValueError("Non valid data type is used in dtypes")

        if use_attr_names:
            # Rename columns with attribute names instead of display names.
            column_map = {
                self.attributes[attr].display_name: attr
                for attr in attributes
            }
            result.rename(columns=column_map, inplace=True)

        return result