RNApolis 0.8.2__py3-none-any.whl → 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rnapolis/adapter.py +11 -67
- rnapolis/annotator.py +23 -55
- rnapolis/common.py +24 -13
- rnapolis/py.typed +0 -0
- rnapolis/tertiary.py +53 -47
- rnapolis/tertiary_v2.py +34 -1
- {rnapolis-0.8.2.dist-info → rnapolis-0.9.0.dist-info}/METADATA +1 -1
- {rnapolis-0.8.2.dist-info → rnapolis-0.9.0.dist-info}/RECORD +12 -11
- {rnapolis-0.8.2.dist-info → rnapolis-0.9.0.dist-info}/WHEEL +1 -1
- {rnapolis-0.8.2.dist-info → rnapolis-0.9.0.dist-info}/entry_points.txt +0 -0
- {rnapolis-0.8.2.dist-info → rnapolis-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {rnapolis-0.8.2.dist-info → rnapolis-0.9.0.dist-info}/top_level.txt +0 -0
rnapolis/adapter.py
CHANGED
@@ -29,8 +29,7 @@ from rnapolis.common import (
|
|
29
29
|
from rnapolis.parser import read_3d_structure
|
30
30
|
from rnapolis.tertiary import (
|
31
31
|
Mapping2D3D,
|
32
|
-
Structure3D,
|
33
|
-
calculate_all_inter_stem_parameters, # Import the new helper function
|
32
|
+
Structure3D, # Import the new helper function
|
34
33
|
)
|
35
34
|
from rnapolis.util import handle_input_file
|
36
35
|
|
@@ -288,7 +287,7 @@ def parse_fr3d_output(file_path: str) -> BaseInteractions:
|
|
288
287
|
BaseInteractions object containing the interactions found by FR3D
|
289
288
|
"""
|
290
289
|
# Initialize the interaction data dictionary
|
291
|
-
interactions_data = {
|
290
|
+
interactions_data: Dict[str, list] = {
|
292
291
|
"base_pairs": [],
|
293
292
|
"stackings": [],
|
294
293
|
"base_ribose_interactions": [],
|
@@ -320,10 +319,8 @@ def process_external_tool_output(
|
|
320
319
|
structure3d: Structure3D,
|
321
320
|
external_file_path: str,
|
322
321
|
tool: ExternalTool,
|
323
|
-
model: Optional[int] = None,
|
324
322
|
find_gaps: bool = False,
|
325
|
-
|
326
|
-
) -> Tuple[Structure2D, List[str], Mapping2D3D]: # Added Mapping2D3D to return tuple
|
323
|
+
) -> Tuple[Structure2D, Mapping2D3D]: # Added Mapping2D3D to return tuple
|
327
324
|
"""
|
328
325
|
Process external tool output and create a secondary structure representation.
|
329
326
|
|
@@ -336,71 +333,15 @@ def process_external_tool_output(
|
|
336
333
|
tool: The external tool that generated the output (FR3D, DSSR, etc.)
|
337
334
|
model: Model number to use (if None, use first model)
|
338
335
|
find_gaps: Whether to detect gaps in the structure
|
339
|
-
all_dot_brackets: Whether to return all possible dot-bracket notations
|
340
336
|
|
341
337
|
Returns:
|
342
|
-
A tuple containing the Structure2D object
|
343
|
-
and the Mapping2D3D object.
|
338
|
+
A tuple containing the Structure2D object and the Mapping2D3D object.
|
344
339
|
"""
|
345
340
|
# Parse external tool output
|
346
341
|
base_interactions = parse_external_output(external_file_path, tool, structure3d)
|
347
342
|
|
348
343
|
# Extract secondary structure using the external tool's interactions
|
349
|
-
return
|
350
|
-
structure3d, base_interactions, model, find_gaps, all_dot_brackets
|
351
|
-
)
|
352
|
-
|
353
|
-
|
354
|
-
def extract_secondary_structure_from_external(
|
355
|
-
tertiary_structure: Structure3D,
|
356
|
-
base_interactions: BaseInteractions,
|
357
|
-
model: Optional[int] = None,
|
358
|
-
find_gaps: bool = False,
|
359
|
-
all_dot_brackets: bool = False,
|
360
|
-
) -> Tuple[Structure2D, List[str], Mapping2D3D]: # Added Mapping2D3D to return tuple
|
361
|
-
"""
|
362
|
-
Create a secondary structure representation using interactions from an external tool.
|
363
|
-
|
364
|
-
Args:
|
365
|
-
tertiary_structure: The 3D structure parsed from PDB/mmCIF
|
366
|
-
base_interactions: Interactions parsed from external tool output
|
367
|
-
model: Model number to use (if None, use all models)
|
368
|
-
find_gaps: Whether to detect gaps in the structure
|
369
|
-
all_dot_brackets: Whether to return all possible dot-bracket notations
|
370
|
-
|
371
|
-
Returns:
|
372
|
-
A tuple containing the Structure2D object, a list of dot-bracket notations,
|
373
|
-
and the Mapping2D3D object.
|
374
|
-
"""
|
375
|
-
mapping = Mapping2D3D(
|
376
|
-
tertiary_structure,
|
377
|
-
base_interactions.basePairs,
|
378
|
-
base_interactions.stackings,
|
379
|
-
find_gaps,
|
380
|
-
)
|
381
|
-
stems, single_strands, hairpins, loops = mapping.bpseq.elements
|
382
|
-
|
383
|
-
# Calculate inter-stem parameters using the helper function
|
384
|
-
inter_stem_params = calculate_all_inter_stem_parameters(mapping)
|
385
|
-
|
386
|
-
structure2d = Structure2D(
|
387
|
-
base_interactions,
|
388
|
-
str(mapping.bpseq),
|
389
|
-
mapping.dot_bracket,
|
390
|
-
mapping.extended_dot_bracket,
|
391
|
-
stems,
|
392
|
-
single_strands,
|
393
|
-
hairpins,
|
394
|
-
loops,
|
395
|
-
inter_stem_params, # Added inter-stem parameters
|
396
|
-
)
|
397
|
-
if all_dot_brackets:
|
398
|
-
return structure2d, mapping.all_dot_brackets, mapping # Return mapping
|
399
|
-
else:
|
400
|
-
return structure2d, [structure2d.dotBracket], mapping # Return mapping
|
401
|
-
|
402
|
-
|
403
|
-
# Removed duplicate functions - now imported from annotator
|
344
|
+
return structure3d.extract_secondary_structure(base_interactions, find_gaps)
|
404
345
|
|
405
346
|
|
406
347
|
def main():
|
@@ -431,15 +372,18 @@ def main():
|
|
431
372
|
structure3d = read_3d_structure(file, None)
|
432
373
|
|
433
374
|
# Process external tool output and get secondary structure
|
434
|
-
structure2d,
|
375
|
+
structure2d, mapping = process_external_tool_output(
|
435
376
|
structure3d,
|
436
377
|
args.external,
|
437
378
|
ExternalTool(args.tool),
|
438
|
-
None,
|
439
379
|
args.find_gaps,
|
440
|
-
args.all_dot_brackets,
|
441
380
|
)
|
442
381
|
|
382
|
+
if args.all_dot_brackets:
|
383
|
+
dot_brackets = mapping.all_dot_brackets
|
384
|
+
else:
|
385
|
+
dot_brackets = [mapping.dot_bracket]
|
386
|
+
|
443
387
|
handle_output_arguments(args, structure2d, dot_brackets, mapping, args.input)
|
444
388
|
|
445
389
|
|
rnapolis/annotator.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#! /usr/bin/env python
|
2
2
|
import argparse
|
3
|
+
import copy
|
3
4
|
import csv
|
4
5
|
import logging
|
5
6
|
import math
|
@@ -43,7 +44,6 @@ from rnapolis.tertiary import (
|
|
43
44
|
Mapping2D3D, # Added import
|
44
45
|
Residue3D,
|
45
46
|
Structure3D,
|
46
|
-
calculate_all_inter_stem_parameters, # Import the new helper function
|
47
47
|
torsion_angle,
|
48
48
|
)
|
49
49
|
from rnapolis.util import handle_input_file
|
@@ -485,41 +485,6 @@ def extract_base_interactions(
|
|
485
485
|
return BaseInteractions(base_pairs, stackings, base_ribose, base_phosphate, [])
|
486
486
|
|
487
487
|
|
488
|
-
def extract_secondary_structure(
|
489
|
-
tertiary_structure: Structure3D,
|
490
|
-
model: Optional[int] = None,
|
491
|
-
find_gaps: bool = False,
|
492
|
-
all_dot_brackets: bool = False,
|
493
|
-
) -> Tuple[Structure2D, List[str]]:
|
494
|
-
base_interactions = extract_base_interactions(tertiary_structure, model)
|
495
|
-
mapping = Mapping2D3D(
|
496
|
-
tertiary_structure,
|
497
|
-
base_interactions.basePairs,
|
498
|
-
base_interactions.stackings,
|
499
|
-
find_gaps,
|
500
|
-
)
|
501
|
-
stems, single_strands, hairpins, loops = mapping.bpseq.elements
|
502
|
-
|
503
|
-
# Calculate inter-stem parameters using the helper function
|
504
|
-
inter_stem_params = calculate_all_inter_stem_parameters(mapping)
|
505
|
-
|
506
|
-
structure2d = Structure2D(
|
507
|
-
base_interactions,
|
508
|
-
str(mapping.bpseq),
|
509
|
-
mapping.dot_bracket,
|
510
|
-
mapping.extended_dot_bracket,
|
511
|
-
stems,
|
512
|
-
single_strands,
|
513
|
-
hairpins,
|
514
|
-
loops,
|
515
|
-
inter_stem_params, # Added inter-stem parameters
|
516
|
-
)
|
517
|
-
if all_dot_brackets:
|
518
|
-
return structure2d, mapping.all_dot_brackets
|
519
|
-
else:
|
520
|
-
return structure2d, [structure2d.dotBracket]
|
521
|
-
|
522
|
-
|
523
488
|
def generate_pymol_script(mapping: Mapping2D3D, stems: List[Stem]) -> str:
|
524
489
|
"""Generates a PyMOL script to draw stems as cylinders."""
|
525
490
|
pymol_commands = []
|
@@ -613,16 +578,26 @@ def generate_pymol_script(mapping: Mapping2D3D, stems: List[Stem]) -> str:
|
|
613
578
|
|
614
579
|
|
615
580
|
def write_json(path: str, structure2d: Structure2D):
|
581
|
+
processed = copy.deepcopy(structure2d)
|
582
|
+
processed.bpseq_index = {
|
583
|
+
k: Residue(v.label, v.auth) for k, v in structure2d.bpseq_index.items()
|
584
|
+
}
|
585
|
+
|
616
586
|
with open(path, "wb") as f:
|
617
587
|
# Add OPT_SERIALIZE_NUMPY to handle numpy types like float64
|
618
|
-
|
588
|
+
# Add OPT_NON_STR_KEYS to preserve integer keys in dictionaries
|
589
|
+
f.write(
|
590
|
+
orjson.dumps(
|
591
|
+
processed, option=orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS
|
592
|
+
)
|
593
|
+
)
|
619
594
|
|
620
595
|
|
621
596
|
def write_csv(path: str, structure2d: Structure2D):
|
622
597
|
with open(path, "w") as f:
|
623
598
|
writer = csv.writer(f)
|
624
599
|
writer.writerow(["nt1", "nt2", "type", "classification-1", "classification-2"])
|
625
|
-
for base_pair in structure2d.
|
600
|
+
for base_pair in structure2d.base_pairs:
|
626
601
|
writer.writerow(
|
627
602
|
[
|
628
603
|
base_pair.nt1.full_name,
|
@@ -636,7 +611,7 @@ def write_csv(path: str, structure2d: Structure2D):
|
|
636
611
|
),
|
637
612
|
]
|
638
613
|
)
|
639
|
-
for stacking in structure2d.
|
614
|
+
for stacking in structure2d.stackings:
|
640
615
|
writer.writerow(
|
641
616
|
[
|
642
617
|
stacking.nt1.full_name,
|
@@ -646,7 +621,7 @@ def write_csv(path: str, structure2d: Structure2D):
|
|
646
621
|
"",
|
647
622
|
]
|
648
623
|
)
|
649
|
-
for base_phosphate in structure2d.
|
624
|
+
for base_phosphate in structure2d.base_phosphate_interactions:
|
650
625
|
writer.writerow(
|
651
626
|
[
|
652
627
|
base_phosphate.nt1.full_name,
|
@@ -656,7 +631,7 @@ def write_csv(path: str, structure2d: Structure2D):
|
|
656
631
|
"",
|
657
632
|
]
|
658
633
|
)
|
659
|
-
for base_ribose in structure2d.
|
634
|
+
for base_ribose in structure2d.base_ribose_interactions:
|
660
635
|
writer.writerow(
|
661
636
|
[
|
662
637
|
base_ribose.nt1.full_name,
|
@@ -666,7 +641,7 @@ def write_csv(path: str, structure2d: Structure2D):
|
|
666
641
|
"",
|
667
642
|
]
|
668
643
|
)
|
669
|
-
for other in structure2d.
|
644
|
+
for other in structure2d.other_interactions:
|
670
645
|
writer.writerow(
|
671
646
|
[
|
672
647
|
other.nt1.full_name,
|
@@ -737,12 +712,12 @@ def handle_output_arguments(
|
|
737
712
|
write_bpseq(args.bpseq, structure2d.bpseq)
|
738
713
|
|
739
714
|
if args.extended:
|
740
|
-
print(structure2d.
|
715
|
+
print(structure2d.extended_dot_bracket)
|
741
716
|
elif args.all_dot_brackets:
|
742
717
|
for dot_bracket in dot_brackets:
|
743
718
|
print(dot_bracket)
|
744
719
|
else:
|
745
|
-
print(structure2d.
|
720
|
+
print(structure2d.dot_bracket)
|
746
721
|
|
747
722
|
if args.dot:
|
748
723
|
print(BpSeq.from_string(structure2d.bpseq).graphviz)
|
@@ -753,7 +728,7 @@ def handle_output_arguments(
|
|
753
728
|
f.write(pml_script)
|
754
729
|
|
755
730
|
if args.inter_stem_csv:
|
756
|
-
if structure2d.
|
731
|
+
if structure2d.inter_stem_parameters:
|
757
732
|
# Convert list of dataclasses to list of dicts
|
758
733
|
params_list = [
|
759
734
|
{
|
@@ -862,16 +837,9 @@ def main():
|
|
862
837
|
|
863
838
|
file = handle_input_file(args.input)
|
864
839
|
structure3d = read_3d_structure(file, None)
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
# Need the mapping object for PML generation
|
870
|
-
mapping = Mapping2D3D(
|
871
|
-
structure3d,
|
872
|
-
structure2d.baseInteractions.basePairs,
|
873
|
-
structure2d.baseInteractions.stackings,
|
874
|
-
args.find_gaps,
|
840
|
+
base_interactions = extract_base_interactions(structure3d)
|
841
|
+
structure2d, dot_brackets, mapping = structure3d.extract_secondary_structure(
|
842
|
+
base_interactions, args.find_gaps, args.all_dot_brackets
|
875
843
|
)
|
876
844
|
|
877
845
|
handle_output_arguments(args, structure2d, dot_brackets, mapping, args.input)
|
rnapolis/common.py
CHANGED
@@ -1021,7 +1021,13 @@ class MultiStrandDotBracket(DotBracket):
|
|
1021
1021
|
strands: List[Strand]
|
1022
1022
|
|
1023
1023
|
@staticmethod
|
1024
|
-
def from_string(
|
1024
|
+
def from_string(sequence: str, structure: str):
|
1025
|
+
# Provide compatibility with DotBracket.from_string
|
1026
|
+
strand = Strand(1, len(sequence), sequence, structure)
|
1027
|
+
return MultiStrandDotBracket(sequence, structure, [strand])
|
1028
|
+
|
1029
|
+
@staticmethod
|
1030
|
+
def from_multiline_string(input: str):
|
1025
1031
|
strands = []
|
1026
1032
|
first = 1
|
1027
1033
|
|
@@ -1045,16 +1051,16 @@ class MultiStrandDotBracket(DotBracket):
|
|
1045
1051
|
@staticmethod
|
1046
1052
|
def from_file(path: str):
|
1047
1053
|
with open(path) as f:
|
1048
|
-
return MultiStrandDotBracket.
|
1054
|
+
return MultiStrandDotBracket.from_multiline_string(f.read())
|
1049
1055
|
|
1050
1056
|
|
1051
1057
|
@dataclass(frozen=True, order=True)
|
1052
1058
|
class BaseInteractions:
|
1053
|
-
|
1059
|
+
base_pairs: List[BasePair]
|
1054
1060
|
stackings: List[Stacking]
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1061
|
+
base_ribose_interactions: List[BaseRibose]
|
1062
|
+
base_phosphate_interactions: List[BasePhosphate]
|
1063
|
+
other_interactions: List[OtherInteraction]
|
1058
1064
|
|
1059
1065
|
|
1060
1066
|
@dataclass(frozen=True, order=True)
|
@@ -1069,14 +1075,19 @@ class InterStemParameters:
|
|
1069
1075
|
coaxial_probability: Optional[float] # Probability of stems being coaxial (0-1)
|
1070
1076
|
|
1071
1077
|
|
1072
|
-
@dataclass
|
1078
|
+
@dataclass
|
1073
1079
|
class Structure2D:
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1080
|
+
base_pairs: List[BasePair]
|
1081
|
+
stackings: List[Stacking]
|
1082
|
+
base_ribose_interactions: List[BaseRibose]
|
1083
|
+
base_phosphate_interactions: List[BasePhosphate]
|
1084
|
+
other_interactions: List[OtherInteraction]
|
1085
|
+
bpseq: BpSeq
|
1086
|
+
bpseq_index: Dict[int, Residue]
|
1087
|
+
dot_bracket: MultiStrandDotBracket
|
1088
|
+
extended_dot_bracket: str
|
1078
1089
|
stems: List[Stem]
|
1079
|
-
|
1090
|
+
single_strands: List[SingleStrand]
|
1080
1091
|
hairpins: List[Hairpin]
|
1081
1092
|
loops: List[Loop]
|
1082
|
-
|
1093
|
+
inter_stem_parameters: List[InterStemParameters]
|
rnapolis/py.typed
ADDED
File without changes
|
rnapolis/tertiary.py
CHANGED
@@ -11,6 +11,7 @@ import numpy.typing
|
|
11
11
|
from scipy.stats import vonmises
|
12
12
|
|
13
13
|
from rnapolis.common import (
|
14
|
+
BaseInteractions,
|
14
15
|
BasePair,
|
15
16
|
BpSeq,
|
16
17
|
Entry,
|
@@ -24,6 +25,7 @@ from rnapolis.common import (
|
|
24
25
|
Stacking,
|
25
26
|
Stem,
|
26
27
|
Strand,
|
28
|
+
Structure2D,
|
27
29
|
)
|
28
30
|
|
29
31
|
BASE_ATOMS = {
|
@@ -456,6 +458,50 @@ class Structure3D:
|
|
456
458
|
return self.residue_map.get(auth)
|
457
459
|
return None
|
458
460
|
|
461
|
+
def extract_secondary_structure(
|
462
|
+
self, base_interactions: BaseInteractions, find_gaps: bool = False
|
463
|
+
) -> Tuple[Structure2D, "Mapping2D3D"]:
|
464
|
+
"""
|
465
|
+
Create a secondary structure representation.
|
466
|
+
|
467
|
+
Args:
|
468
|
+
base_interactions: Interactions
|
469
|
+
find_gaps: Whether to detect gaps in the structure
|
470
|
+
all_dot_brackets: Whether to return all possible dot-bracket notations
|
471
|
+
|
472
|
+
Returns:
|
473
|
+
A tuple containing the Structure2D object, a list of dot-bracket notations,
|
474
|
+
and the Mapping2D3D object.
|
475
|
+
"""
|
476
|
+
mapping = Mapping2D3D(
|
477
|
+
self,
|
478
|
+
base_interactions.base_pairs,
|
479
|
+
base_interactions.stackings,
|
480
|
+
find_gaps,
|
481
|
+
)
|
482
|
+
stems, single_strands, hairpins, loops = mapping.bpseq.elements
|
483
|
+
|
484
|
+
# Calculate inter-stem parameters using the helper function
|
485
|
+
inter_stem_params = calculate_all_inter_stem_parameters(mapping)
|
486
|
+
|
487
|
+
structure2d = Structure2D(
|
488
|
+
base_interactions.base_pairs,
|
489
|
+
base_interactions.stackings,
|
490
|
+
base_interactions.base_ribose_interactions,
|
491
|
+
base_interactions.base_phosphate_interactions,
|
492
|
+
base_interactions.other_interactions,
|
493
|
+
mapping.bpseq,
|
494
|
+
mapping.bpseq_index_to_residue_map,
|
495
|
+
mapping.dot_bracket,
|
496
|
+
mapping.extended_dot_bracket,
|
497
|
+
stems,
|
498
|
+
single_strands,
|
499
|
+
hairpins,
|
500
|
+
loops,
|
501
|
+
inter_stem_params,
|
502
|
+
)
|
503
|
+
return structure2d, mapping
|
504
|
+
|
459
505
|
|
460
506
|
@dataclass
|
461
507
|
class Mapping2D3D:
|
@@ -559,46 +605,6 @@ class Mapping2D3D:
|
|
559
605
|
|
560
606
|
@cached_property
|
561
607
|
def bpseq(self) -> BpSeq:
|
562
|
-
def pair_scoring_function(pair: BasePair3D) -> int:
|
563
|
-
if pair.saenger is not None:
|
564
|
-
if pair.saenger in (Saenger.XIX, Saenger.XX):
|
565
|
-
return 0, pair.nt1, pair.nt2
|
566
|
-
else:
|
567
|
-
return 1, pair.nt1, pair.nt2
|
568
|
-
|
569
|
-
sequence = "".join(
|
570
|
-
sorted(
|
571
|
-
[
|
572
|
-
pair.nt1_3d.one_letter_name.upper(),
|
573
|
-
pair.nt2_3d.one_letter_name.upper(),
|
574
|
-
]
|
575
|
-
)
|
576
|
-
)
|
577
|
-
if sequence in ("AU", "AT", "CG"):
|
578
|
-
return 0, pair.nt1, pair.nt2
|
579
|
-
return 1, pair.nt1, pair.nt2
|
580
|
-
|
581
|
-
canonical = [
|
582
|
-
base_pair
|
583
|
-
for base_pair in self.base_pairs
|
584
|
-
if base_pair.is_canonical and base_pair.nt1 < base_pair.nt2
|
585
|
-
]
|
586
|
-
|
587
|
-
while True:
|
588
|
-
matches = defaultdict(set)
|
589
|
-
|
590
|
-
for base_pair in canonical:
|
591
|
-
matches[base_pair.nt1_3d].add(base_pair)
|
592
|
-
matches[base_pair.nt2_3d].add(base_pair)
|
593
|
-
|
594
|
-
for pairs in matches.values():
|
595
|
-
if len(pairs) > 1:
|
596
|
-
pairs = sorted(pairs, key=pair_scoring_function)
|
597
|
-
canonical.remove(pairs[-1])
|
598
|
-
break
|
599
|
-
else:
|
600
|
-
break
|
601
|
-
|
602
608
|
return self._generated_bpseq_data[0]
|
603
609
|
|
604
610
|
@cached_property
|
@@ -613,9 +619,9 @@ class Mapping2D3D:
|
|
613
619
|
def pair_scoring_function(pair: BasePair3D) -> int:
|
614
620
|
if pair.saenger is not None:
|
615
621
|
if pair.saenger in (Saenger.XIX, Saenger.XX):
|
616
|
-
return 0
|
622
|
+
return 0
|
617
623
|
else:
|
618
|
-
return 1
|
624
|
+
return 1
|
619
625
|
|
620
626
|
sequence = "".join(
|
621
627
|
sorted(
|
@@ -626,8 +632,8 @@ class Mapping2D3D:
|
|
626
632
|
)
|
627
633
|
)
|
628
634
|
if sequence in ("AU", "AT", "CG"):
|
629
|
-
return 0
|
630
|
-
return 1
|
635
|
+
return 0
|
636
|
+
return 1
|
631
637
|
|
632
638
|
canonical = [
|
633
639
|
base_pair
|
@@ -636,11 +642,11 @@ class Mapping2D3D:
|
|
636
642
|
]
|
637
643
|
|
638
644
|
while True:
|
639
|
-
matches = defaultdict(
|
645
|
+
matches = defaultdict(list)
|
640
646
|
|
641
647
|
for base_pair in canonical:
|
642
|
-
matches[base_pair.nt1_3d].
|
643
|
-
matches[base_pair.nt2_3d].
|
648
|
+
matches[base_pair.nt1_3d].append(base_pair)
|
649
|
+
matches[base_pair.nt2_3d].append(base_pair)
|
644
650
|
|
645
651
|
for pairs in matches.values():
|
646
652
|
if len(pairs) > 1:
|
rnapolis/tertiary_v2.py
CHANGED
@@ -121,7 +121,9 @@ class Structure:
|
|
121
121
|
groupby_cols.append("pdbx_PDB_ins_code")
|
122
122
|
|
123
123
|
# Group atoms by residue
|
124
|
-
grouped = self.atoms.groupby(
|
124
|
+
grouped = self.atoms.groupby(
|
125
|
+
groupby_cols, dropna=False, observed=False, sort=False
|
126
|
+
)
|
125
127
|
|
126
128
|
else:
|
127
129
|
# For unknown formats, return an empty list
|
@@ -495,6 +497,37 @@ class Residue:
|
|
495
497
|
return Atom(atoms_df.iloc[0], self.format)
|
496
498
|
return None
|
497
499
|
|
500
|
+
@cached_property
|
501
|
+
def is_nucleotide(self) -> bool:
|
502
|
+
"""
|
503
|
+
Check if this residue is a nucleotide.
|
504
|
+
|
505
|
+
A nucleotide is identified by the presence of specific atoms:
|
506
|
+
- Sugar atoms: C1', C2', C3', C4', O4'
|
507
|
+
- Base atoms: N1, C2, N3, C4, C5, C6
|
508
|
+
|
509
|
+
Returns:
|
510
|
+
--------
|
511
|
+
bool
|
512
|
+
True if the residue is a nucleotide, False otherwise
|
513
|
+
"""
|
514
|
+
# Early check: if less than 11 atoms, can't be a nucleotide
|
515
|
+
if len(self.atoms) < 11:
|
516
|
+
return False
|
517
|
+
|
518
|
+
# Required sugar atoms
|
519
|
+
sugar_atoms = ["C1'", "C2'", "C3'", "C4'", "O4'"]
|
520
|
+
|
521
|
+
# Required base atoms
|
522
|
+
base_atoms = ["N1", "C2", "N3", "C4", "C5", "C6"]
|
523
|
+
|
524
|
+
# Check for all required atoms
|
525
|
+
for atom_name in sugar_atoms + base_atoms:
|
526
|
+
if self.find_atom(atom_name) is None:
|
527
|
+
return False
|
528
|
+
|
529
|
+
return True
|
530
|
+
|
498
531
|
def is_connected(self, next_residue_candidate: "Residue") -> bool:
|
499
532
|
"""
|
500
533
|
Check if this residue is connected to the next residue candidate.
|
@@ -1,8 +1,8 @@
|
|
1
|
-
rnapolis/adapter.py,sha256=
|
1
|
+
rnapolis/adapter.py,sha256=b4fyHq0Dt9VLsxungiI-a4o1t-JqSw3Sd_loFYXGaUk,13371
|
2
2
|
rnapolis/aligner.py,sha256=o7rQyjAZ3n4VXcnSPY3HVB8nLNRkVbl552O3NVh0mfg,3429
|
3
|
-
rnapolis/annotator.py,sha256=
|
3
|
+
rnapolis/annotator.py,sha256=4sDNQOvipxa-Axlu7MTvYUFinWh26CW2-wcHrP1Gcuo,30796
|
4
4
|
rnapolis/clashfinder.py,sha256=AC9_tIx7QIk57sELq_aKfU1u3UMOXbgcccQeGHhMR6c,8517
|
5
|
-
rnapolis/common.py,sha256=
|
5
|
+
rnapolis/common.py,sha256=HTe-RSZa_9hEIi-j4-1afxdqt7zAD-BpZ7JxRZGX170,32390
|
6
6
|
rnapolis/component_A.csv,sha256=koirS-AwUZwoYGItT8yn3wS6Idvmh2FANfTQcOS_xh8,2897
|
7
7
|
rnapolis/component_C.csv,sha256=NtvsAu_YrUgTjzZm3j4poW4IZ99x3dPARB09XVIiMCc,2803
|
8
8
|
rnapolis/component_G.csv,sha256=Z5wl8OnHRyx4XhTyBiWgRZiEvmZXhoxtVRH8bn6Vxf0,2898
|
@@ -13,16 +13,17 @@ rnapolis/molecule_filter.py,sha256=jgcpJxx_oXEBX0d30v4k_FdwRouRUPUsEtCYWgLGpD4,7
|
|
13
13
|
rnapolis/motif_extractor.py,sha256=Lfn1iEkhkP9eZD3GPEWNAfy00QO7QPCc8wM_XS1ory8,1147
|
14
14
|
rnapolis/parser.py,sha256=3g4mtFvpiEENFcSBBtx_E_x1vJPF9BujWnts0kb9XjE,16340
|
15
15
|
rnapolis/parser_v2.py,sha256=qG6CO3or7zmuJu368g9Nzokiqdeip4yjD14F163uH6w,40618
|
16
|
+
rnapolis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
17
|
rnapolis/rfam_folder.py,sha256=SjiiyML_T1__saruFwSMJEoQ7Y55GIU8ktS8ZUn5-fw,11111
|
17
18
|
rnapolis/splitter.py,sha256=x-Zn21mkiMgvYPptUFD9BbdNIvoaM6b8GzGf6uYXEwE,4052
|
18
|
-
rnapolis/tertiary.py,sha256=
|
19
|
-
rnapolis/tertiary_v2.py,sha256=
|
19
|
+
rnapolis/tertiary.py,sha256=zGRhza-GJLI_gmKGjnvPksvm3EjOi7O263ngckvE4rs,39408
|
20
|
+
rnapolis/tertiary_v2.py,sha256=y7Rh43Jzt9QU6wCa1wAHIcO3BcNQY83PbbWNTmqI8zM,23424
|
20
21
|
rnapolis/transformer.py,sha256=aC0nBmHHJf5TyLvBIV57Jj3tlwpvHbPo347opfAOlQA,3844
|
21
22
|
rnapolis/unifier.py,sha256=2ge7IB9FdRgzSAiVD39U_ciwtdDJ2fGzf8mUIudbrqY,5820
|
22
23
|
rnapolis/util.py,sha256=IdquFO3PV1_KDqodjupzm0Rqvgy0CeSzxGHaGEHYXVU,543
|
23
|
-
rnapolis-0.
|
24
|
-
rnapolis-0.
|
25
|
-
rnapolis-0.
|
26
|
-
rnapolis-0.
|
27
|
-
rnapolis-0.
|
28
|
-
rnapolis-0.
|
24
|
+
rnapolis-0.9.0.dist-info/licenses/LICENSE,sha256=ZGRu12MzCgbYA-Lt8MyBlmjvPZh7xfiD5u5wBx0enq4,1066
|
25
|
+
rnapolis-0.9.0.dist-info/METADATA,sha256=SmNBsfi66LNJ4MGjgR8ovdhocCRlNmYV2eR74vsLLL4,54537
|
26
|
+
rnapolis-0.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
27
|
+
rnapolis-0.9.0.dist-info/entry_points.txt,sha256=H00KoN54wU3dFOofAu3H_3PADmZOBTB1hXf5TUU2uzo,438
|
28
|
+
rnapolis-0.9.0.dist-info/top_level.txt,sha256=LcO18koxZcWoJ21KDRRRo_tyIbmXL5z61dPitZpy8yc,9
|
29
|
+
rnapolis-0.9.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|