Skip to content

Commit 094c1bc

Browse files
more robust fix and tests
1 parent 5a90a6a commit 094c1bc

2 files changed

Lines changed: 147 additions & 6 deletions

File tree

R/dataframe.R

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,47 @@
2323
over_origin <- dat$start > dat$end & dat$direction == 1
2424

2525
if (any(over_origin)) {
26-
offset <- dat$end[over_origin] + 1
27-
2826
if (is.null(bp)) {
2927
bp <- max(c(dat$start, dat$end))
3028
}
31-
29+
30+
# For origin-spanning features like join(4891..5096,1..751):
31+
# - We need to offset the coordinate system so this becomes continuous
32+
# - The offset should be chosen so that the feature becomes [new_start..new_end]
33+
# - All other features get shifted by the same offset
34+
35+
# Find the origin-spanning feature with the smallest end coordinate
36+
# This determines our offset
37+
min_end <- min(dat$end[over_origin])
38+
offset <- min_end
39+
40+
# Apply offset to all coordinates
3241
dat$start <- dat$start - offset
3342
dat$end <- dat$end - offset
34-
35-
dat$end[over_origin] <- bp
36-
43+
44+
# For origin-spanning features, calculate the correct end position
45+
# Original: join(4891..5096, 1..751) with bp=5096
46+
# After offset by 751: start=4140, end=0
47+
# Correct end should be: start + ((5096-4891+1) + 751 - 1) = 4140 + 956 = 5096
48+
for (i in which(over_origin)) {
49+
original_start <- dat$start[i] + offset # Restore original start
50+
original_end <- dat$end[i] + offset # Restore original end
51+
52+
# Calculate total feature length: (bp - start + 1) + end
53+
part1_length <- bp - original_start + 1 # From start to end of plasmid
54+
part2_length <- original_end # From beginning to end position
55+
total_length <- part1_length + part2_length
56+
57+
# Set the new end position
58+
dat$end[i] <- dat$start[i] + total_length - 1
59+
}
60+
61+
# Handle any negative coordinates by wrapping them around
62+
negative_coords <- dat$start < 0 | dat$end < 0
63+
dat$start[negative_coords & dat$start < 0] <-
64+
dat$start[negative_coords & dat$start < 0] + bp
65+
dat$end[negative_coords & dat$end < 0] <-
66+
dat$end[negative_coords & dat$end < 0] + bp
3767
}
3868

3969
# only return features where a start was successfully parsed

tests/testthat/test-get_features.R

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,114 @@ test_that("Extract over origin", {
7777
1
7878
)
7979
})
80+
81+
test_that("Origin-spanning feature detection and offset", {
82+
# Create test data with origin-spanning feature
83+
test_features <- list(
84+
list(
85+
type = "CDS",
86+
name = "TurboID",
87+
start_end = c(4891, 751),
88+
direction = 1
89+
),
90+
list(
91+
type = "gene",
92+
name = "normal_gene",
93+
start_end = c(1000, 2000),
94+
direction = 1
95+
)
96+
)
97+
98+
# Test with bp = 5096 (plasmid length)
99+
df <- .feature_list_to_df(test_features, bp = 5096)
100+
101+
# Check that origin-spanning feature is detected
102+
expect_true(any(df$start > df$end))
103+
104+
# After offset, no feature should have start > end
105+
expect_true(all(df$start <= df$end))
106+
107+
# Check that the TurboID feature spans correctly
108+
turboid <- df[df$name == "TurboID", ]
109+
expect_true(nrow(turboid) == 1)
110+
expect_true(turboid$start < turboid$end)
111+
expect_true(turboid$end > turboid$start)
112+
})
113+
114+
test_that("Multiple origin-spanning features", {
115+
test_features <- list(
116+
list(
117+
type = "CDS",
118+
name = "feature1",
119+
start_end = c(4500, 500),
120+
direction = 1
121+
),
122+
list(
123+
type = "CDS",
124+
name = "feature2",
125+
start_end = c(4800, 300),
126+
direction = 1
127+
),
128+
list(
129+
type = "gene",
130+
name = "normal_feature",
131+
start_end = c(1000, 2000),
132+
direction = 1
133+
)
134+
)
135+
136+
df <- .feature_list_to_df(test_features, bp = 5000)
137+
138+
# All features should have valid coordinates after offset
139+
expect_true(all(df$start <= df$end))
140+
expect_true(all(df$start >= 0))
141+
expect_true(all(df$end >= df$start))
142+
})
143+
144+
test_that("Complement origin-spanning features", {
145+
test_features <- list(
146+
list(
147+
type = "CDS",
148+
name = "complement_feature",
149+
start_end = c(4891, 751),
150+
direction = -1 # complement
151+
)
152+
)
153+
154+
df <- .feature_list_to_df(test_features, bp = 5096)
155+
156+
# Complement features should not trigger offset logic
157+
# (only direction = 1 features should)
158+
complement_feat <- df[df$name == "complement_feature", ]
159+
expect_true(nrow(complement_feat) == 1)
160+
})
161+
162+
test_that("Real plasmid file with origin-spanning feature", {
163+
# Test the actual problematic file
164+
file_path <- system.file("extdata", "559763_pLann.txt", package = "plasmapR")
165+
166+
if (file.exists(file_path)) {
167+
plasmid <- read_gb(file_path)
168+
df <- as.data.frame(plasmid)
169+
170+
# Find the TurboID feature
171+
turboid <- df[df$name == "TurboID", ]
172+
expect_true(nrow(turboid) == 1)
173+
174+
# After processing, it should have valid coordinates
175+
expect_true(turboid$start < turboid$end)
176+
expect_true(turboid$start >= 0)
177+
expect_true(turboid$end <= max(df$end))
178+
179+
# The feature should span a reasonable length
180+
# Original was join(4891..5096,1..751) = (5096-4891) + 751 = 956 bp
181+
expected_length <- (5096 - 4891 + 1) + 751 # 957 bp
182+
actual_length <- turboid$end - turboid$start + 1
183+
184+
# Allow some tolerance for offset calculations
185+
expect_true(abs(actual_length - expected_length) <= 10,
186+
info = paste("Expected ~", expected_length, "got", actual_length))
187+
} else {
188+
skip("Test file not found")
189+
}
190+
})

0 commit comments

Comments
 (0)