ciffer 0.1.0__tar.gz
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.
- ciffer-0.1.0/.github/workflows/lint.yml +19 -0
- ciffer-0.1.0/.github/workflows/publish.yml +20 -0
- ciffer-0.1.0/.github/workflows/test.yml +21 -0
- ciffer-0.1.0/.gitignore +7 -0
- ciffer-0.1.0/.pre-commit-config.yaml +29 -0
- ciffer-0.1.0/.python-version +1 -0
- ciffer-0.1.0/LICENSE +21 -0
- ciffer-0.1.0/PKG-INFO +180 -0
- ciffer-0.1.0/README.md +156 -0
- ciffer-0.1.0/pyproject.toml +61 -0
- ciffer-0.1.0/src/ciffer/__init__.py +21 -0
- ciffer-0.1.0/src/ciffer/cif/__init__.py +333 -0
- ciffer-0.1.0/src/ciffer/cif/altloc.py +79 -0
- ciffer-0.1.0/src/ciffer/cif/assembly.py +121 -0
- ciffer-0.1.0/src/ciffer/cif/bonds.py +275 -0
- ciffer-0.1.0/src/ciffer/cif/category.py +151 -0
- ciffer-0.1.0/src/ciffer/cif/hierarchy.py +415 -0
- ciffer-0.1.0/src/ciffer/cif/metadata.py +166 -0
- ciffer-0.1.0/src/ciffer/cif/pdb.py +77 -0
- ciffer-0.1.0/src/ciffer/cif/rcsb.py +28 -0
- ciffer-0.1.0/src/ciffer/cif/writable.py +315 -0
- ciffer-0.1.0/src/ciffer/py.typed +0 -0
- ciffer-0.1.0/src/ciffer/serialize.py +100 -0
- ciffer-0.1.0/src/ciffer/structure.py +340 -0
- ciffer-0.1.0/src/ciffer/table.py +337 -0
- ciffer-0.1.0/src/ciffer/view.py +374 -0
- ciffer-0.1.0/tests/data/1EJG.cif.gz +0 -0
- ciffer-0.1.0/tests/data/1G2J.cif.gz +0 -0
- ciffer-0.1.0/tests/data/1L2Y_3models.cif.gz +0 -0
- ciffer-0.1.0/tests/data/1M4X.cif.gz +0 -0
- ciffer-0.1.0/tests/data/1STP.cif.gz +0 -0
- ciffer-0.1.0/tests/data/1STP.pdb.gz +0 -0
- ciffer-0.1.0/tests/data/4DWA.cif.gz +0 -0
- ciffer-0.1.0/tests/test_cif.py +758 -0
- ciffer-0.1.0/tests/test_structure.py +256 -0
- ciffer-0.1.0/tests/test_table.py +90 -0
- ciffer-0.1.0/uv.lock +580 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v5
|
|
14
|
+
with:
|
|
15
|
+
enable-cache: true
|
|
16
|
+
- run: uv sync --locked
|
|
17
|
+
- run: uv run ruff check
|
|
18
|
+
- run: uv run ruff format --check
|
|
19
|
+
- run: uv run ty check
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write # PyPI trusted publishing
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v5
|
|
17
|
+
- run: uv sync --locked
|
|
18
|
+
- run: uv run pytest
|
|
19
|
+
- run: uv build
|
|
20
|
+
- run: uv publish
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python: ["3.11", "3.12", "3.13", "3.14"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v5
|
|
17
|
+
with:
|
|
18
|
+
enable-cache: true
|
|
19
|
+
python-version: ${{ matrix.python }}
|
|
20
|
+
- run: uv sync --locked
|
|
21
|
+
- run: uv run pytest
|
ciffer-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
|
|
10
|
+
- repo: local
|
|
11
|
+
hooks:
|
|
12
|
+
- id: ruff-check
|
|
13
|
+
name: ruff check
|
|
14
|
+
entry: uv run --no-sync ruff check --fix
|
|
15
|
+
language: system
|
|
16
|
+
types_or: [python, pyi]
|
|
17
|
+
require_serial: true
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
name: ruff format
|
|
20
|
+
entry: uv run --no-sync ruff format
|
|
21
|
+
language: system
|
|
22
|
+
types_or: [python, pyi, markdown]
|
|
23
|
+
require_serial: true
|
|
24
|
+
- id: ty
|
|
25
|
+
name: ty check
|
|
26
|
+
entry: uv run --no-sync ty check
|
|
27
|
+
language: system
|
|
28
|
+
types_or: [python, pyi]
|
|
29
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
ciffer-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Howon Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ciffer-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ciffer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Biomolecular structures as NumPy tables, ready for ML pipelines.
|
|
5
|
+
Project-URL: Repository, https://github.com/Buddha7771/ciffer
|
|
6
|
+
Project-URL: Issues, https://github.com/Buddha7771/ciffer/issues
|
|
7
|
+
Author-email: Howon Lee <hwlee7771@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: machine-learning,mmcif,numpy,pdb,protein,structure
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: gemmi>=0.7.5
|
|
21
|
+
Requires-Dist: numpy>=2.2
|
|
22
|
+
Requires-Dist: zstandard>=0.25.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# ciffer
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/ciffer/) [](https://github.com/Buddha7771/ciffer/actions/workflows/test.yml)
|
|
28
|
+
|
|
29
|
+
Biomolecular structures as NumPy tables, ready for ML pipelines.
|
|
30
|
+
|
|
31
|
+
Every structure is three linked tables (atoms, residues, and chains) whose columns are NumPy arrays. Built on [gemmi](https://github.com/project-gemmi/gemmi).
|
|
32
|
+
|
|
33
|
+
- **Read and write in one line.** mmCIF and PDB, with models, altlocs, and assemblies chosen as you read.
|
|
34
|
+
- **Navigate like PyMOL.** Select atoms, hop to their residues and chains, and extract what you keep.
|
|
35
|
+
- **Handle columns like pandas.** Every column is a NumPy array, and your own columns go in with `[]`.
|
|
36
|
+
- **Save once, load as is.** `to_bytes()` keeps every column, bond, and metadata entry.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip install ciffer
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
ciffer needs Python 3.11 or newer.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### A first look
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import ciffer
|
|
52
|
+
|
|
53
|
+
st = ciffer.fetch("1stp", assembly=True) # from RCSB, as the biological tetramer
|
|
54
|
+
|
|
55
|
+
st.atoms.name[:4] # ['N', 'CA', 'C', 'O']
|
|
56
|
+
st.atoms.xyz.shape # (4004, 3)
|
|
57
|
+
len(st.atoms.bond) # 3280
|
|
58
|
+
|
|
59
|
+
st.residues.name[:4] # ['ASP', 'PRO', 'SER', 'LYS']
|
|
60
|
+
st.residues.label_seq_id[:4] # [1, 2, 3, 4]
|
|
61
|
+
|
|
62
|
+
st.chains.label_asym_id[:3] # ['A1', 'B1', 'C1']
|
|
63
|
+
st.chains.entity_type[:3] # ['polymer', 'non-polymer', 'water']
|
|
64
|
+
|
|
65
|
+
st.metadata["resolution"] # 2.6
|
|
66
|
+
st.metadata["release_date"] # '1992-10-15'
|
|
67
|
+
|
|
68
|
+
st # prints every column, edge, and metadata key
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
<details>
|
|
72
|
+
<summary>Output of <code>st</code></summary>
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
<Structure atoms=4004 residues=976 chains=12>
|
|
76
|
+
atoms
|
|
77
|
+
name <U3
|
|
78
|
+
element <U1
|
|
79
|
+
altloc <U1
|
|
80
|
+
occupancy float64
|
|
81
|
+
b_factor float64
|
|
82
|
+
charge int64
|
|
83
|
+
xyz float64 (3,)
|
|
84
|
+
edges
|
|
85
|
+
bond 3280 edges
|
|
86
|
+
order int64
|
|
87
|
+
aromatic bool
|
|
88
|
+
struct_conn 0 edges
|
|
89
|
+
type <U1
|
|
90
|
+
order int64
|
|
91
|
+
residues
|
|
92
|
+
name <U3
|
|
93
|
+
label_seq_id int64
|
|
94
|
+
auth_seq_id <U3
|
|
95
|
+
ins_code <U1
|
|
96
|
+
comp_type <U17
|
|
97
|
+
chains
|
|
98
|
+
label_asym_id <U2
|
|
99
|
+
auth_asym_id <U2
|
|
100
|
+
entity_id <U1
|
|
101
|
+
entity_type <U11
|
|
102
|
+
polymer_type <U14
|
|
103
|
+
description <U32
|
|
104
|
+
metadata
|
|
105
|
+
id '1STP'
|
|
106
|
+
title 'STRUCTURAL ORIGINS OF HIGH-AFFINITY BIOTIN BINDING TO STREPTAVIDIN'
|
|
107
|
+
keywords 'BIOTIN BINDING PROTEIN'
|
|
108
|
+
deposit_date '1992-03-12'
|
|
109
|
+
methods ['X-RAY DIFFRACTION']
|
|
110
|
+
authors ['Weber, P.C.', 'Salemme, F.R.']
|
|
111
|
+
resolution 2.6
|
|
112
|
+
release_date '1992-10-15'
|
|
113
|
+
model 1
|
|
114
|
+
models [1]
|
|
115
|
+
assembly '1'
|
|
116
|
+
assemblies ['1']
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
</details>
|
|
120
|
+
|
|
121
|
+
### Read and write
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
ensemble = ciffer.fetch("1l2y", directory="data", model="all") # cached in data/
|
|
125
|
+
len(ensemble) # 38
|
|
126
|
+
ciffer.write(ensemble[0], "trp_cage.pdb.gz")
|
|
127
|
+
ciffer.read("trp_cage.pdb.gz").atoms.xyz.shape # (304, 3)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`read` takes `.cif`, `.mmcif`, `.pdb`, and `.ent`, gzipped or not. `fetch` also takes extended ids such as `pdb_00001stp`. Both accept `model`, `altloc`, and `assembly`.
|
|
131
|
+
|
|
132
|
+
### Select and navigate
|
|
133
|
+
|
|
134
|
+
Select rows with a mask, hop between levels, and `extract()` what you keep:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import numpy as np
|
|
138
|
+
|
|
139
|
+
biotin = st.residues[st.residues.name == "BTN"].atoms
|
|
140
|
+
protein = st.chains[st.chains.entity_type == "polymer"].atoms
|
|
141
|
+
distance = np.linalg.norm(protein.xyz[:, None] - biotin.xyz, axis=-1).min(axis=1)
|
|
142
|
+
pocket = protein[distance < 5].residues
|
|
143
|
+
pocket.name # ['ASN', 'LEU', 'SER', 'TYR', 'SER', 'VAL', ...]
|
|
144
|
+
pocket.chains.auth_asym_id # ['A1', 'A2', 'A3', 'A4']
|
|
145
|
+
ciffer.write(pocket.extract(), "pocket.cif")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Label and edit
|
|
149
|
+
|
|
150
|
+
Add your own column with `[]`, select with it, and edit through the whole level:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
st.atoms["pocket"] = np.isin(st.atoms.indices, pocket.atoms.indices)
|
|
154
|
+
ca = st.atoms[st.atoms.name == "CA"]
|
|
155
|
+
len(ca[ca["pocket"]]) # 72
|
|
156
|
+
noise = np.random.default_rng(0).normal(scale=0.5, size=(len(pocket.atoms), 3))
|
|
157
|
+
st.atoms.xyz[pocket.atoms.indices] += noise # writes into the structure
|
|
158
|
+
# A selection such as `ca` holds copies, so `ca.xyz += 1` raises a TypeError.
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Save and load
|
|
162
|
+
|
|
163
|
+
Store a structure as bytes in any database, such as SQLite, and load it back:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import sqlite3
|
|
167
|
+
|
|
168
|
+
db = sqlite3.connect("structures.db")
|
|
169
|
+
db.execute("CREATE TABLE IF NOT EXISTS structures (id TEXT PRIMARY KEY, data BLOB)")
|
|
170
|
+
db.execute("REPLACE INTO structures VALUES (?, ?)", ("1stp", st.to_bytes()))
|
|
171
|
+
db.commit()
|
|
172
|
+
|
|
173
|
+
(data,) = db.execute("SELECT data FROM structures WHERE id = '1stp'").fetchone()
|
|
174
|
+
st = ciffer.Structure.from_bytes(data)
|
|
175
|
+
int(st.atoms["pocket"].sum()) # 616
|
|
176
|
+
len(st.atoms.bond) # 3280
|
|
177
|
+
st.metadata["resolution"] # 2.6
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Your column, the bonds, and the metadata all come back.
|
ciffer-0.1.0/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# ciffer
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/ciffer/) [](https://github.com/Buddha7771/ciffer/actions/workflows/test.yml)
|
|
4
|
+
|
|
5
|
+
Biomolecular structures as NumPy tables, ready for ML pipelines.
|
|
6
|
+
|
|
7
|
+
Every structure is three linked tables (atoms, residues, and chains) whose columns are NumPy arrays. Built on [gemmi](https://github.com/project-gemmi/gemmi).
|
|
8
|
+
|
|
9
|
+
- **Read and write in one line.** mmCIF and PDB, with models, altlocs, and assemblies chosen as you read.
|
|
10
|
+
- **Navigate like PyMOL.** Select atoms, hop to their residues and chains, and extract what you keep.
|
|
11
|
+
- **Handle columns like pandas.** Every column is a NumPy array, and your own columns go in with `[]`.
|
|
12
|
+
- **Save once, load as is.** `to_bytes()` keeps every column, bond, and metadata entry.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pip install ciffer
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
ciffer needs Python 3.11 or newer.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### A first look
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import ciffer
|
|
28
|
+
|
|
29
|
+
st = ciffer.fetch("1stp", assembly=True) # from RCSB, as the biological tetramer
|
|
30
|
+
|
|
31
|
+
st.atoms.name[:4] # ['N', 'CA', 'C', 'O']
|
|
32
|
+
st.atoms.xyz.shape # (4004, 3)
|
|
33
|
+
len(st.atoms.bond) # 3280
|
|
34
|
+
|
|
35
|
+
st.residues.name[:4] # ['ASP', 'PRO', 'SER', 'LYS']
|
|
36
|
+
st.residues.label_seq_id[:4] # [1, 2, 3, 4]
|
|
37
|
+
|
|
38
|
+
st.chains.label_asym_id[:3] # ['A1', 'B1', 'C1']
|
|
39
|
+
st.chains.entity_type[:3] # ['polymer', 'non-polymer', 'water']
|
|
40
|
+
|
|
41
|
+
st.metadata["resolution"] # 2.6
|
|
42
|
+
st.metadata["release_date"] # '1992-10-15'
|
|
43
|
+
|
|
44
|
+
st # prints every column, edge, and metadata key
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
<details>
|
|
48
|
+
<summary>Output of <code>st</code></summary>
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
<Structure atoms=4004 residues=976 chains=12>
|
|
52
|
+
atoms
|
|
53
|
+
name <U3
|
|
54
|
+
element <U1
|
|
55
|
+
altloc <U1
|
|
56
|
+
occupancy float64
|
|
57
|
+
b_factor float64
|
|
58
|
+
charge int64
|
|
59
|
+
xyz float64 (3,)
|
|
60
|
+
edges
|
|
61
|
+
bond 3280 edges
|
|
62
|
+
order int64
|
|
63
|
+
aromatic bool
|
|
64
|
+
struct_conn 0 edges
|
|
65
|
+
type <U1
|
|
66
|
+
order int64
|
|
67
|
+
residues
|
|
68
|
+
name <U3
|
|
69
|
+
label_seq_id int64
|
|
70
|
+
auth_seq_id <U3
|
|
71
|
+
ins_code <U1
|
|
72
|
+
comp_type <U17
|
|
73
|
+
chains
|
|
74
|
+
label_asym_id <U2
|
|
75
|
+
auth_asym_id <U2
|
|
76
|
+
entity_id <U1
|
|
77
|
+
entity_type <U11
|
|
78
|
+
polymer_type <U14
|
|
79
|
+
description <U32
|
|
80
|
+
metadata
|
|
81
|
+
id '1STP'
|
|
82
|
+
title 'STRUCTURAL ORIGINS OF HIGH-AFFINITY BIOTIN BINDING TO STREPTAVIDIN'
|
|
83
|
+
keywords 'BIOTIN BINDING PROTEIN'
|
|
84
|
+
deposit_date '1992-03-12'
|
|
85
|
+
methods ['X-RAY DIFFRACTION']
|
|
86
|
+
authors ['Weber, P.C.', 'Salemme, F.R.']
|
|
87
|
+
resolution 2.6
|
|
88
|
+
release_date '1992-10-15'
|
|
89
|
+
model 1
|
|
90
|
+
models [1]
|
|
91
|
+
assembly '1'
|
|
92
|
+
assemblies ['1']
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
</details>
|
|
96
|
+
|
|
97
|
+
### Read and write
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
ensemble = ciffer.fetch("1l2y", directory="data", model="all") # cached in data/
|
|
101
|
+
len(ensemble) # 38
|
|
102
|
+
ciffer.write(ensemble[0], "trp_cage.pdb.gz")
|
|
103
|
+
ciffer.read("trp_cage.pdb.gz").atoms.xyz.shape # (304, 3)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`read` takes `.cif`, `.mmcif`, `.pdb`, and `.ent`, gzipped or not. `fetch` also takes extended ids such as `pdb_00001stp`. Both accept `model`, `altloc`, and `assembly`.
|
|
107
|
+
|
|
108
|
+
### Select and navigate
|
|
109
|
+
|
|
110
|
+
Select rows with a mask, hop between levels, and `extract()` what you keep:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import numpy as np
|
|
114
|
+
|
|
115
|
+
biotin = st.residues[st.residues.name == "BTN"].atoms
|
|
116
|
+
protein = st.chains[st.chains.entity_type == "polymer"].atoms
|
|
117
|
+
distance = np.linalg.norm(protein.xyz[:, None] - biotin.xyz, axis=-1).min(axis=1)
|
|
118
|
+
pocket = protein[distance < 5].residues
|
|
119
|
+
pocket.name # ['ASN', 'LEU', 'SER', 'TYR', 'SER', 'VAL', ...]
|
|
120
|
+
pocket.chains.auth_asym_id # ['A1', 'A2', 'A3', 'A4']
|
|
121
|
+
ciffer.write(pocket.extract(), "pocket.cif")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Label and edit
|
|
125
|
+
|
|
126
|
+
Add your own column with `[]`, select with it, and edit through the whole level:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
st.atoms["pocket"] = np.isin(st.atoms.indices, pocket.atoms.indices)
|
|
130
|
+
ca = st.atoms[st.atoms.name == "CA"]
|
|
131
|
+
len(ca[ca["pocket"]]) # 72
|
|
132
|
+
noise = np.random.default_rng(0).normal(scale=0.5, size=(len(pocket.atoms), 3))
|
|
133
|
+
st.atoms.xyz[pocket.atoms.indices] += noise # writes into the structure
|
|
134
|
+
# A selection such as `ca` holds copies, so `ca.xyz += 1` raises a TypeError.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Save and load
|
|
138
|
+
|
|
139
|
+
Store a structure as bytes in any database, such as SQLite, and load it back:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import sqlite3
|
|
143
|
+
|
|
144
|
+
db = sqlite3.connect("structures.db")
|
|
145
|
+
db.execute("CREATE TABLE IF NOT EXISTS structures (id TEXT PRIMARY KEY, data BLOB)")
|
|
146
|
+
db.execute("REPLACE INTO structures VALUES (?, ?)", ("1stp", st.to_bytes()))
|
|
147
|
+
db.commit()
|
|
148
|
+
|
|
149
|
+
(data,) = db.execute("SELECT data FROM structures WHERE id = '1stp'").fetchone()
|
|
150
|
+
st = ciffer.Structure.from_bytes(data)
|
|
151
|
+
int(st.atoms["pocket"].sum()) # 616
|
|
152
|
+
len(st.atoms.bond) # 3280
|
|
153
|
+
st.metadata["resolution"] # 2.6
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Your column, the bonds, and the metadata all come back.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ciffer"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Biomolecular structures as NumPy tables, ready for ML pipelines."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [{ name = "Howon Lee", email = "hwlee7771@gmail.com" }]
|
|
9
|
+
keywords = ["mmcif", "pdb", "protein", "structure", "numpy", "machine-learning"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Science/Research",
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
dependencies = ["gemmi>=0.7.5", "numpy>=2.2", "zstandard>=0.25.0"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Repository = "https://github.com/Buddha7771/ciffer"
|
|
24
|
+
Issues = "https://github.com/Buddha7771/ciffer/issues"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = ["pre-commit>=4.6", "pytest>=9.0.3", "ruff==0.17.0", "ty==0.0.88"]
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel]
|
|
34
|
+
packages = ["src/ciffer"]
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
line-length = 88
|
|
38
|
+
target-version = "py311"
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
select = ["ALL"]
|
|
42
|
+
extend-select = [
|
|
43
|
+
"D417",
|
|
44
|
+
] # Disabled by the numpy convention; keep Parameters complete
|
|
45
|
+
ignore = [
|
|
46
|
+
"COM812", # Conflicts with ruff formatter
|
|
47
|
+
"CPY001", # Missing copyright notice
|
|
48
|
+
"D100", # Missing docstring in public module
|
|
49
|
+
"D104", # Missing docstring in public package
|
|
50
|
+
"ANN401", # Dynamically typed expressions (typing.Any)
|
|
51
|
+
"PLR0913", # Too many arguments in function definition
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint.per-file-ignores]
|
|
55
|
+
"tests/*" = ["ANN201", "D", "INP001", "PLR2004", "S101"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint.pydocstyle]
|
|
58
|
+
convention = "numpy"
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .cif import fetch, read, write
|
|
2
|
+
from .structure import Structure, concat
|
|
3
|
+
from .table import EdgeList, Table
|
|
4
|
+
from .view import AtomView, Bond, ChainView, Level, ResidueView, StructConn, View
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AtomView",
|
|
8
|
+
"Bond",
|
|
9
|
+
"ChainView",
|
|
10
|
+
"EdgeList",
|
|
11
|
+
"Level",
|
|
12
|
+
"ResidueView",
|
|
13
|
+
"StructConn",
|
|
14
|
+
"Structure",
|
|
15
|
+
"Table",
|
|
16
|
+
"View",
|
|
17
|
+
"concat",
|
|
18
|
+
"fetch",
|
|
19
|
+
"read",
|
|
20
|
+
"write",
|
|
21
|
+
]
|