<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def read_gtf( filepath_or_buffer, expand_attribute_column=True, infer_biotype_column=False, column_converters={}, usecols=None, features=None, chunksize=1024 * 1024):
 """ Parse a GTF into a dictionary mapping column names to sequences of values. Parameters filepath_or_buffer : str or buffer object Path to GTF file (may be gzip compressed) or buffer object such as StringIO expand_attribute_column : bool Replace strings of semi-colon separated key-value values in the 'attribute' column with one column per distinct key, with a list of values for each row (using None for rows where key didn't occur). infer_biotype_column : bool Due to the annoying ambiguity of the second GTF column across multiple Ensembl releases, figure out if an older GTF's source column is actually the gene_biotype or transcript_biotype. column_converters : dict, optional Dictionary mapping column names to conversion functions. Will replace empty strings with None and otherwise passes them to given conversion function. usecols : list of str or None Restrict which columns are loaded to the give set. If None, then load all columns. features : set of str or None Drop rows which aren't one of the features in the supplied set chunksize : int """

    if isinstance(filepath_or_buffer, string_types) and not exists(filepath_or_buffer):
        raise ValueError("GTF file does not exist: %s" % filepath_or_buffer)

    if expand_attribute_column:
        result_df = parse_gtf_and_expand_attributes(
            filepath_or_buffer,
            chunksize=chunksize,
            restrict_attribute_columns=usecols)
    else:
        result_df = parse_gtf(result_df, features=features)

    for column_name, column_type in list(column_converters.items()):
        result_df[column_name] = [
            column_type(string_value) if len(string_value) > 0 else None
            for string_value
            in result_df[column_name]
        ]

    # Hackishly infer whether the values in the 'source' column of this GTF
    # are actually representing a biotype by checking for the most common
    # gene_biotype and transcript_biotype value 'protein_coding'
    if infer_biotype_column:
        unique_source_values = set(result_df["source"])
        if "protein_coding" in unique_source_values:
            column_names = set(result_df.columns)
            # Disambiguate between the two biotypes by checking if
            # gene_biotype is already present in another column. If it is,
            # the 2nd column is the transcript_biotype (otherwise, it's the
            # gene_biotype)
            if "gene_biotype" not in column_names:
                logging.info("Using column 'source' to replace missing 'gene_biotype'")
                result_df["gene_biotype"] = result_df["source"]
            if "transcript_biotype" not in column_names:
                logging.info("Using column 'source' to replace missing 'transcript_biotype'")
                result_df["transcript_biotype"] = result_df["source"]

    if usecols is not None:
        column_names = set(result_df.columns)
        valid_columns = [c for c in usecols if c in column_names]
        result_df = result_df[valid_columns]

    return result_df