Coming from pyrle¶
runlength replaces pyrle. It's a new library rather than a new version, so the API differs. This page lists what changed and how to translate pyrle code.
What's different¶
- Any value type. pyrle stored every value as
float64. runlength keeps the type you give it: integer coverage stays integer, and strings, booleans, dates and nulls work. - Positions start at 0, and every sequence covers
0up to its length, as in pyrle. - Batches have keys of any type. pyrle's
RleDictwas keyed by chromosome, or by chromosome and strand. AnRleBatchtakes any key columns. Strands are never combined implicitly: pyrle added up the strands when a strandedRleDictmet an unstranded one, while runlength asks for an explicitcollapse(drop="Strand"). - Your own operations. pyrle supported a fixed set of operators. In
runlength,
zip_with,mapand plans let any function combine sequences; see Combining sequences. - Parallelism is automatic. pyrle's
nb_cpuargument and its ray backend are gone. Batch operations use all cores. - Correct statistics. pyrle's
mean()andstd()ignored run lengths: for the sequence0 1 1 1, pyrle's mean is 0.25. runlength counts every position, so it's 0.75. - NaN merges with NaN, so a stretch of NaN is one run instead of one run per original interval.
- No pandas requirement. pyrle needed pandas; runlength needs none of polars, pandas, numpy or pyarrow, and works with all of them.
Translation table¶
| pyrle | runlength |
|---|---|
Rle(runs, values) |
rl.Rle(runs, values) (keeps the value type) |
rle.runs, rle.values |
rle.runs, rle.values (Arrow arrays; np.asarray(...) for numpy) |
rle.length |
len(rle) |
len(rle) (the number of runs) |
rle.num_runs |
rle[i], rle[a:b] |
rle[i], rle[a:b] |
rle + other, -, *, /, comparisons |
the same operators |
rle.defragment() |
rle.merge() (most results are already merged) |
rle.apply_values(f) |
rle.map(f, backend="numpy") |
rle.apply_runs(f) |
rl.Rle(f(np.asarray(rle.runs)), rle.values) |
rle.numbers_only() |
rle.map(np.nan_to_num, backend="numpy") |
rle.shift(dist, fill=0) |
rle.shift(dist, fill=0) |
rle.mean(), rle.std() |
rle.mean(), rle.std() (weighted by run length) |
rle.to_frame() |
rle.to_pandas() (columns start, end, value) |
rle.to_csv(...) |
rle.to_pandas().to_csv(...) |
RleDict(df) / RleDict(df, stranded=True) |
rl.coverage_by_key(df[["Chromosome"]], ...) / df[["Chromosome", "Strand"]] |
RleDict(df, value_col="Score") |
rl.coverage_by_key(keys, df["Start"], df["End"], values=df["Score"]) |
RleDict({...}) |
rl.RleBatch.from_rles(keys, rles) |
rledict["chr1"], rledict["chr1", "+"] |
batch.get("chr1"), batch.get(("chr1", "+")) |
rledict.chromosomes, rledict.keys() |
batch.keys (a table) |
rledict.items() |
batch.items() |
rledict + other (and other operators) |
the same operators (an outer join on keys) |
stranded + unstranded RleDict |
stranded.collapse(drop="Strand") + unstranded |
rledict.make_strands_same_length() |
not needed: shorter sequences are padded |
rledict.add_pseudocounts(0.01) |
batch.map(lambda v: np.where(v == 0, 0.01, v), backend="numpy") |
rledict.to_table() |
batch.to_pandas() |
rledict.to_ranges() |
batch.to_pandas(), then drop rows where value == 0 |
nb_cpu=... |
not needed |
An example¶
Coverage per chromosome and strand, in reads per million, as pyranges computes it. With pyrle:
from pyrle import RleDict
cov = RleDict(df, stranded=True) * (1e6 / len(df))
With runlength:
>>> import pandas as pd
>>> import runlength as rl
>>> df = pd.DataFrame({
... "Chromosome": ["chr1", "chr1", "chr2"],
... "Strand": ["+", "-", "+"],
... "Start": [0, 2, 5],
... "End": [3, 4, 7],
... })
>>> cov = rl.coverage_by_key(df[["Chromosome", "Strand"]], df["Start"], df["End"]) * (1e6 / len(df))
>>> cov
RleBatch(keys=3, runs=5, dtype=Float64)
('chr1', '+'): length=3, runs=[3], values=[333333.3333333333]
('chr1', '-'): length=4, runs=[2, 2], values=[0.0, 333333.3333333333]
('chr2', '+'): length=7, runs=[5, 2], values=[0.0, 333333.3333333333]
>>> cov.to_pandas().query("value != 0")
Chromosome Strand start end value
0 chr1 + 0 3 333333.333333
2 chr1 - 2 4 333333.333333
4 chr2 + 5 7 333333.333333