def create_missing_features(<EOL>dataframe,<EOL>unique_keys={},<EOL>extra_columns={},<EOL>missing_value=None):
extra_dataframes = []<EOL>existing_features = set(dataframe["<STR_LIT>"])<EOL>existing_columns = set(dataframe.keys())<EOL>for (feature_name, groupby_key) in unique_keys.items():<EOL><INDENT>if feature_name in existing_features:<EOL><INDENT>logging.info(<EOL>"<STR_LIT>" % feature_name)<EOL>continue<EOL><DEDENT>logging.info("<STR_LIT>" % feature_name)<EOL>empty_key_values = dataframe[groupby_key].map(<EOL>lambda x: x == "<STR_LIT>" or x is None)<EOL>row_groups = dataframe[~empty_key_values].groupby(groupby_key)<EOL>feature_values = OrderedDict([<EOL>(column_name, [missing_value] * row_groups.ngroups)<EOL>for column_name in dataframe.keys()<EOL>])<EOL>feature_columns = list(extra_columns.get(feature_name, []))<EOL>for i, (feature_id, group) in enumerate(row_groups):<EOL><INDENT>feature_values["<STR_LIT>"][i] = feature_name<EOL>feature_values[groupby_key][i] = feature_id<EOL>feature_values["<STR_LIT:source>"][i] = "<STR_LIT>"<EOL>feature_values["<STR_LIT:start>"][i] = group["<STR_LIT:start>"].min()<EOL>feature_values["<STR_LIT:end>"][i] = group["<STR_LIT:end>"].max()<EOL>feature_values["<STR_LIT>"][i] = group["<STR_LIT>"].iat[<NUM_LIT:0>]<EOL>feature_values["<STR_LIT>"][i] = group["<STR_LIT>"].iat[<NUM_LIT:0>]<EOL>for column_name in feature_columns:<EOL><INDENT>if column_name not in existing_columns:<EOL><INDENT>raise ValueError(<EOL>"<STR_LIT>" % (<EOL>column_name, existing_columns))<EOL><DEDENT>unique_values = group[column_name].dropna().unique()<EOL>if len(unique_values) == <NUM_LIT:1>:<EOL><INDENT>feature_values[column_name][i] = unique_values[<NUM_LIT:0>]<EOL><DEDENT><DEDENT><DEDENT>extra_dataframes.append(pd.DataFrame(feature_values))<EOL><DEDENT>return pd.concat([dataframe] + extra_dataframes, ignore_index=True)<EOL>
Helper function used to construct a missing feature such as 'transcript'
or 'gene'. Some GTF files only have 'exon' and 'CDS' entries, but have
transcript_id and gene_id annotations which allow us to construct those
missing features.

Parameters
----------
dataframe : pandas.DataFrame
    Should contain at least the core GTF columns, such as "seqname",
    "start", and "end"

unique_keys : dict
    Mapping from feature names to the name of the column which should
    act as a unique key for that feature. Example: {"gene": "gene_id"}

extra_columns : dict
    By default the constructed feature row will include only the 8
    core columns and its unique key. Any other columns that should
    be included should be associated with the feature name in this
    dict.

missing_value : any
    Which value to fill in for columns that we don't infer values for.

Returns original dataframe along with all extra rows created for missing
features.