def read_attributes(self, attributes=None):
        '''
        Collect read attributes across reads in this PileupCollection into a
        pandas.DataFrame.
        Valid attributes are the following properties of a pysam.AlignedSegment
        instance. See:
            http://pysam.readthedocs.org/en/latest/api.html
        for the meaning of these attributes.
             * cigarstring
             * flag
             * inferred_length
             * is_duplicate
             * is_paired
             * is_proper_pair
             * is_qcfail
             * is_read1
             * is_read2
             * is_reverse
             * is_secondary
             * is_unmapped
             * mapping_quality
             * mate_is_reverse
             * mate_is_unmapped
             * next_reference_id
             * next_reference_start
             * query_alignment_end
             * query_alignment_length
             * query_alignment_qualities
             * query_alignment_sequence
             * query_alignment_start
             * query_length
             * query_name
             * reference_end
             * reference_id
             * reference_length
             * reference_start
             * template_length
        (Note: the above list is parsed into the _READ_ATTRIBUTE_NAMES class
        variable, so be careful when modifying it.)
        Additionally, for alignment "tags" (arbitrary key values associated
        with an alignment), a column of the form "TAG_{tag name}" is
        included.
        Finally, the column "pysam_alignment_record" gives the underlying
        `pysam.AlignedSegment` instances.
        Parameters
        ----------
        attributes (optional): list of strings
            List of columns to include. If unspecified, all columns are
            included in the result.
        Returns
        ----------
        pandas.DataFrame of read attributes.
        '''
        def include(attribute):
            return attributes is None or attribute in attributes
        reads = self.reads()
        possible_column_names = list(PileupCollection._READ_ATTRIBUTE_NAMES)
        result = OrderedDict(
            (name, [getattr(read, name) for read in reads])
            for name in PileupCollection._READ_ATTRIBUTE_NAMES
            if include(name))
        if reads:
            tag_dicts = [dict(x.get_tags()) for x in reads]
            tag_keys = set.union(
                *[set(item.keys()) for item in tag_dicts])
            for tag_key in sorted(tag_keys):
                column_name = "TAG_%s" % tag_key
                possible_column_names.append(column_name)
                if include(column_name):
                    result[column_name] = [d.get(tag_key) for d in tag_dicts]
        possible_column_names.append("pysam_alignment_record")
        if include("pysam_alignment_record"):
            result["pysam_alignment_record"] = reads
        if attributes is not None:
            for attribute in attributes:
                if attribute not in result:
                    raise ValueError(
                        "No such attribute: %s. Valid attributes are: %s"
                        % (attribute, " ".join(possible_column_names)))
            assert set(attributes) == set(result)
        return pandas.DataFrame(result)