Comma-separated case-insensitive substring filter, parsed once and matched
many times. Used by --select-row-type / --remove-row-type /
--hto-row-type so callers can pass e.g. "gene,peak" to match either
“Gene Expression” or “Peaks”.
Combine feature IDs and names into composite id_name strings.
If a name is empty or already equals the ID (e.g. 10x ATAC peaks where
both features/id and features/name are chr1:1000-2000), the ID is
used as-is to avoid chr1:1000-2000_chr1:1000-2000 duplication.
Inverse-document-frequency marker weight ln(C / df): a gene claimed by
all C types gets weight 0 (removed from scoring), a type-exclusive gene
the maximum ln(C).
Inverse of compose_id_name: split a composite id{ROW_SEP}name display
name back into (id, name) on the first ROW_SEP. When there is no
separator (a bare symbol, or an id-only composite where name was empty or
equalled id) both parts are the whole string, so a 10x features.tsv still
gets a non-empty gene name.