studio2ped 0.1.0__tar.gz → 0.2.1__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.
- studio2ped-0.2.1/CHANGELOG.md +88 -0
- studio2ped-0.2.1/MANIFEST.in +4 -0
- studio2ped-0.2.1/PKG-INFO +244 -0
- studio2ped-0.2.1/README.md +221 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/pyproject.toml +11 -9
- studio2ped-0.2.1/studio2ped/__init__.py +10 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped/cli.py +17 -4
- {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped/converter.py +370 -53
- studio2ped-0.2.1/studio2ped.egg-info/PKG-INFO +244 -0
- studio2ped-0.2.1/studio2ped.egg-info/SOURCES.txt +30 -0
- studio2ped-0.2.1/studio2ped.egg-info/requires.txt +3 -0
- studio2ped-0.2.1/studio2ped_standalone.py +1099 -0
- studio2ped-0.2.1/tests/data/cf-cousins.studio.json +508 -0
- studio2ped-0.2.1/tests/data/expected/cf-cousins.mped +16 -0
- studio2ped-0.2.1/tests/data/expected/features.mped +29 -0
- studio2ped-0.2.1/tests/data/expected/lynch.mped +22 -0
- studio2ped-0.2.1/tests/data/expected/okafor.mped +18 -0
- studio2ped-0.2.1/tests/data/expected/price.mped +18 -0
- studio2ped-0.2.1/tests/data/features.studio.json +1044 -0
- studio2ped-0.2.1/tests/data/lynch.studio.json +712 -0
- studio2ped-0.2.1/tests/data/okafor.studio.json +589 -0
- studio2ped-0.2.1/tests/data/price.studio.json +582 -0
- studio2ped-0.2.1/tests/test_standalone.py +45 -0
- studio2ped-0.2.1/tests/test_studio2ped.py +288 -0
- studio2ped-0.2.1/tests/test_version.py +13 -0
- studio2ped-0.2.1/tools/make_standalone.py +76 -0
- studio2ped-0.1.0/PKG-INFO +0 -157
- studio2ped-0.1.0/README.md +0 -131
- studio2ped-0.1.0/studio2ped/__init__.py +0 -7
- studio2ped-0.1.0/studio2ped.egg-info/PKG-INFO +0 -157
- studio2ped-0.1.0/studio2ped.egg-info/SOURCES.txt +0 -12
- {studio2ped-0.1.0 → studio2ped-0.2.1}/LICENSE +0 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/setup.cfg +0 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped/__main__.py +0 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped.egg-info/dependency_links.txt +0 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped.egg-info/entry_points.txt +0 -0
- {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
- A parent marked G is a gestational carrier, and is not written as a genetic
|
|
6
|
+
parent. Since the 2025 correction to the pedigree nomenclature (Steinhaus
|
|
7
|
+
French, Bennett and Resta, *J Genet Couns* 2025;34(2):e2020), G marks a
|
|
8
|
+
gestational carrier, and Pedigree Studio draws G where it used to draw S.
|
|
9
|
+
S, which older drawings use, still means the same.
|
|
10
|
+
- The note says "gestational carrier" and gives the mark (G or S), not
|
|
11
|
+
"surrogate": the corrected nomenclature keeps "surrogate" for a woman who
|
|
12
|
+
gives the egg and carries the pregnancy, who is a genetic parent.
|
|
13
|
+
- A symbol divided into thirds, which Pedigree Studio now draws for three
|
|
14
|
+
conditions, gives each of its colours as a condition the person has, as
|
|
15
|
+
quarters do.
|
|
16
|
+
- The "no children" and "infertility" symbols no longer count when a row is
|
|
17
|
+
numbered. The nomenclature numbers individuals (Bennett et al., *J Genet
|
|
18
|
+
Couns* 2022;31(6):1238-48) and these symbols are not people, so Pedigree
|
|
19
|
+
Studio now gives them no number, and studio2ped numbers only people too:
|
|
20
|
+
someone to the right of one of these symbols in the same row has a number
|
|
21
|
+
one lower than before (II-3 rather than II-4), and a row with nothing but
|
|
22
|
+
these symbols is not a generation.
|
|
23
|
+
|
|
24
|
+
## 0.2.0
|
|
25
|
+
|
|
26
|
+
- People are now identified by their pedigree numbers (I-1, II-3 and so on),
|
|
27
|
+
worked out exactly as Pedigree Studio shows them and as studio2ga4gh 0.4
|
|
28
|
+
writes them, so the same person has the same identifier in the PED file and
|
|
29
|
+
in the GA4GH files. Version 0.1.0 took identifiers from the text under each
|
|
30
|
+
symbol, which usually holds a name and an age (`Alice_50`), sometimes a
|
|
31
|
+
condition (`Bowel_cancer`), and is not always unique: in the Lynch example
|
|
32
|
+
two cousins labelled 26 got the same identifier, which `mpedtools validate`
|
|
33
|
+
rejects.
|
|
34
|
+
- `--ids` chooses the identifiers: `numbers` (the default), `drawing` (the
|
|
35
|
+
labels the drawing shows under its numbering setting: numbers, letters or
|
|
36
|
+
labels typed in), `studio` (Pedigree Studio's own ids) or `text` (0.1.0's
|
|
37
|
+
identifiers). Whatever the choice, an identifier that would be shared by two
|
|
38
|
+
people, be empty or be `0` (which PED reads as "no parent") is replaced by
|
|
39
|
+
the person's pedigree number, and the command says so.
|
|
40
|
+
- The "no children" and "infertility" symbols are no longer written as
|
|
41
|
+
people. Pedigree Studio stores them as children of the couple, so 0.1.0 put
|
|
42
|
+
them in the file as individuals of unknown sex (29 individuals for the 27
|
|
43
|
+
people in the features example). They are left out now, with a note naming
|
|
44
|
+
the couple.
|
|
45
|
+
- Adoptive parents and surrogates are no longer written as parents. A child
|
|
46
|
+
joined to a couple by a dashed line (an adoption) and a parent marked S (a
|
|
47
|
+
gestational carrier) are not genetic relations, and a linkage program reads
|
|
48
|
+
the father and mother columns as genetic parents, so 0.1.0's files gave
|
|
49
|
+
wrong ones. Those parents are now 0, with a note, which is also what
|
|
50
|
+
studio2ga4gh's PED-style pedigree gives: a solid line from birth parents
|
|
51
|
+
still counts, and so does a donor (D).
|
|
52
|
+
- A symbol standing for several people (a number such as 3 in the symbol) is
|
|
53
|
+
still written as one person, but the command now says so.
|
|
54
|
+
- An ontology id written after a condition's name in the colour key, as
|
|
55
|
+
studio2ga4gh reads it (`Condition X [MONDO:0009061]`), is left out of the
|
|
56
|
+
MPED column name, which is now `Condition_X` instead of
|
|
57
|
+
`Condition_X_MONDO0009061`. Only the ids of ontologies count (the HPO,
|
|
58
|
+
Mondo, OMIM, Orphanet, SNOMED CT and the others studio2ga4gh can declare):
|
|
59
|
+
anything else in brackets, such as `Hypertension (BP:160/95)`, stays part of
|
|
60
|
+
the name. Two conditions that would give the same column name are told apart
|
|
61
|
+
with `_2`, `_3`.
|
|
62
|
+
- Without `-o`, the output is named after the input without `.studio.json`:
|
|
63
|
+
`okafor.studio.json` gives `okafor.mped`, where 0.1.0 wrote
|
|
64
|
+
`okafor.studio.mped`.
|
|
65
|
+
- Within each generation, people are listed in the order of the drawing,
|
|
66
|
+
from left to right, rather than in the order of Pedigree Studio's internal
|
|
67
|
+
ids.
|
|
68
|
+
- Session files are read as UTF-8 (with or without a byte-order mark) and the
|
|
69
|
+
PED files written as UTF-8, whatever the computer's own setting, so names
|
|
70
|
+
with accents no longer fail on Windows.
|
|
71
|
+
- `-d` makes the output folder if it does not exist. In 0.1.0 the README's own
|
|
72
|
+
example (`-d ./output/`) failed unless the folder was already there, and
|
|
73
|
+
the error said that the input file could not be found.
|
|
74
|
+
- `studio2ped_standalone.py` is made from the package by
|
|
75
|
+
`tools/make_standalone.py`, and a test checks that it is up to date and
|
|
76
|
+
writes exactly what the package writes.
|
|
77
|
+
- New in the Python interface: `convert(..., ids=...)`,
|
|
78
|
+
`convert_file(..., ids=...)`, `individual_ids`, `pedigree_numbers` and
|
|
79
|
+
`ID_CHOICES`; `ConversionResult` has `notes` and `ids`.
|
|
80
|
+
- `--version`, tests run on Python 3.9 to 3.14, and releases are published to
|
|
81
|
+
PyPI from GitHub.
|
|
82
|
+
|
|
83
|
+
Commands written for 0.1.0 still run unchanged. To get 0.1.0's identifiers,
|
|
84
|
+
add `--ids text`.
|
|
85
|
+
|
|
86
|
+
## 0.1.0
|
|
87
|
+
|
|
88
|
+
Released on PyPI on 20 May 2026.
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: studio2ped
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Convert Pedigree Studio session JSON to PED/MPED pedigree files
|
|
5
|
+
Author-email: Tim Hearn <tjh70@cam.ac.uk>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/comparativechrono/studio2ped
|
|
8
|
+
Project-URL: Issues, https://github.com/comparativechrono/studio2ped/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/comparativechrono/studio2ped/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: pedigree,genetics,ped,family-tree,clinical-genetics,pedigree-studio
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Provides-Extra: test
|
|
21
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# studio2ped
|
|
25
|
+
|
|
26
|
+
Convert families drawn in [Pedigree Studio](https://github.com/comparativechrono/PedigreeStudio)
|
|
27
|
+
into standard PED pedigree files, or into extended MPED (multi-phenotype PED)
|
|
28
|
+
files when more than one condition is recorded.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install studio2ped
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
It needs Python 3.9 or later and nothing else. You can also use the
|
|
37
|
+
standalone script, `studio2ped_standalone.py`, without installing anything.
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Command line
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Basic conversion: writes okafor.ped, or okafor.mped if the family needs MPED
|
|
45
|
+
studio2ped okafor.studio.json
|
|
46
|
+
|
|
47
|
+
# Specify output name and directory
|
|
48
|
+
studio2ped okafor.studio.json -o my_pedigree -d ./output/
|
|
49
|
+
|
|
50
|
+
# Identify people by the labels the drawing shows, instead of pedigree numbers
|
|
51
|
+
studio2ped okafor.studio.json --ids drawing
|
|
52
|
+
|
|
53
|
+
# Using the standalone script
|
|
54
|
+
python studio2ped_standalone.py okafor.studio.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The command prints a short summary, the files it wrote and notes on anything
|
|
58
|
+
you should know: symbols that are not people and were left out, identifiers
|
|
59
|
+
that had to be changed, and so on.
|
|
60
|
+
|
|
61
|
+
Commands written for version 0.1.0 still work. The difference you will notice
|
|
62
|
+
is that people are now identified by their pedigree numbers; add `--ids text`
|
|
63
|
+
to get the identifiers 0.1.0 used (see [Identifying people](#identifying-people)).
|
|
64
|
+
|
|
65
|
+
### Python API
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import json
|
|
69
|
+
import studio2ped
|
|
70
|
+
|
|
71
|
+
with open("okafor.studio.json", encoding="utf-8") as f:
|
|
72
|
+
session = json.load(f)
|
|
73
|
+
|
|
74
|
+
result = studio2ped.convert(session, "okafor") # ids="drawing", "studio" or "text" to choose identifiers
|
|
75
|
+
print(result.summary)
|
|
76
|
+
for note in result.notes:
|
|
77
|
+
print("Note:", note)
|
|
78
|
+
for filename, content in result.files:
|
|
79
|
+
print(f"--- {filename} ---")
|
|
80
|
+
print(content)
|
|
81
|
+
|
|
82
|
+
# From file to file
|
|
83
|
+
result = studio2ped.convert_file("okafor.studio.json")
|
|
84
|
+
for filepath, _ in result.files:
|
|
85
|
+
print(f"Written: {filepath}")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## What it does
|
|
89
|
+
|
|
90
|
+
The converter reads a Pedigree Studio session file and:
|
|
91
|
+
|
|
92
|
+
1. **Detects separate pedigrees**: if the canvas holds several unconnected
|
|
93
|
+
families, each is written to a file of its own.
|
|
94
|
+
2. **Works out the family structure** from the partnership and child lines,
|
|
95
|
+
not from where the symbols are, so a rearranged pedigree converts correctly.
|
|
96
|
+
3. **Leaves out symbols that are not people.** Pedigree Studio stores the "no
|
|
97
|
+
children" bar and the "infertility" double bar as children of the couple;
|
|
98
|
+
they are not written as individuals, and a note names the couple.
|
|
99
|
+
4. **Gives genetic parents only.** The father and mother columns of a PED
|
|
100
|
+
file are genetic parents. A child joined to a couple by a dashed line (an
|
|
101
|
+
adoption) gets 0 for that couple, and so does the place of a parent marked
|
|
102
|
+
G (a gestational carrier), with a note in each case; a parent marked D (a
|
|
103
|
+
donor) is a genetic parent. Drawings made before the 2025 correction to the
|
|
104
|
+
pedigree nomenclature mark a gestational carrier S, which is read the same
|
|
105
|
+
way. studio2ga4gh's PED-style pedigree gives the same.
|
|
106
|
+
5. **Extracts phenotypes** from the colour key: solid fills, half fills,
|
|
107
|
+
symbols in thirds or quarters, and shading patterns are all recognised.
|
|
108
|
+
6. **Detects carrier notation**: centre dots (●) and half-filled symbols mark
|
|
109
|
+
carriers.
|
|
110
|
+
7. **Chooses the format**:
|
|
111
|
+
- **one phenotype** → a standard `.ped` file (6 columns);
|
|
112
|
+
- **several phenotypes**, or **carrier notation alongside a phenotype** → an
|
|
113
|
+
extended `.mped` file.
|
|
114
|
+
|
|
115
|
+
## Identifying people
|
|
116
|
+
|
|
117
|
+
| `--ids` | Each person is identified by |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `numbers` (the default) | their pedigree number as Pedigree Studio shows it: the generation in Roman numerals and the position from the left, such as `II-3` |
|
|
120
|
+
| `drawing` | the label the drawing shows under its numbering setting: pedigree numbers, letters (`II-C`, or `A`, `B`, `C` …) or labels typed in |
|
|
121
|
+
| `studio` | Pedigree Studio's own id, such as `p-3` |
|
|
122
|
+
| `text` | what version 0.1.0 used: the label typed in if there is one, otherwise the first line of the text under the symbol, otherwise Pedigree Studio's id |
|
|
123
|
+
|
|
124
|
+
Pedigree numbers are the ones studio2ga4gh writes into the GA4GH files, so a
|
|
125
|
+
person can be followed from one file to the other. The text under a symbol
|
|
126
|
+
usually holds a name and an age ("Alice, 50"), so `text`, and `drawing` when
|
|
127
|
+
the drawing has labels typed in, can put names into the file: use them only
|
|
128
|
+
when that is acceptable for wherever the file is going.
|
|
129
|
+
|
|
130
|
+
Identifiers can't contain spaces, so spaces become underscores and other
|
|
131
|
+
punctuation is dropped: "J. Mensah" becomes `J_Mensah`. studio2ga4gh makes its
|
|
132
|
+
ids from labels the same way, so with the same choice a person has the same
|
|
133
|
+
identifier in the PED file and in the GA4GH files. No two people may share an
|
|
134
|
+
identifier, and `0` means "no parent" in a PED file, so if a label would be
|
|
135
|
+
shared by two people (two cousins both labelled "26", say), would be empty or
|
|
136
|
+
would be `0`, the person is given their pedigree number instead, and a note
|
|
137
|
+
says so.
|
|
138
|
+
|
|
139
|
+
Pedigree numbers follow the drawing, as in Pedigree Studio: a new generation
|
|
140
|
+
starts 80 pixels or more below the first person of the generation above, and
|
|
141
|
+
each generation is numbered from the left. Only people are numbered: the "no
|
|
142
|
+
children" and "infertility" symbols are not people, so they have no number
|
|
143
|
+
and do not count in their row.
|
|
144
|
+
|
|
145
|
+
## Output formats
|
|
146
|
+
|
|
147
|
+
### Standard PED (`.ped`)
|
|
148
|
+
|
|
149
|
+
The standard 6-column format used by PLINK, GATK and most genetics tools:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
# Family: FAM1
|
|
153
|
+
# FamID IndID FatherID MotherID Sex Phenotype
|
|
154
|
+
FAM1 I-1 0 0 1 1
|
|
155
|
+
FAM1 I-2 0 0 2 1
|
|
156
|
+
FAM1 II-1 I-1 I-2 2 2
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected. The columns
|
|
160
|
+
are separated by tabs.
|
|
161
|
+
|
|
162
|
+
### Extended MPED (`.mped`)
|
|
163
|
+
|
|
164
|
+
A multi-phenotype extension for pedigrees in which more than one condition is
|
|
165
|
+
tracked. It adds a header line naming the phenotype columns, and a carrier
|
|
166
|
+
status:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
# MPED v1 Breast_cancer Carrier
|
|
170
|
+
# FamID IndID FatherID MotherID Sex Breast_cancer Carrier
|
|
171
|
+
FAM1 I-1 0 0 1 1 1
|
|
172
|
+
FAM1 I-2 0 0 2 3 3
|
|
173
|
+
FAM1 II-1 I-1 I-2 2 2 1
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected, `3` = carrier.
|
|
177
|
+
|
|
178
|
+
The format is easy to read by program: the first line declares the format and
|
|
179
|
+
names the phenotype columns, the second is a header for people to read, and
|
|
180
|
+
the data lines have the same tab-separated columns as standard PED, with one
|
|
181
|
+
more column for each phenotype.
|
|
182
|
+
|
|
183
|
+
Column names are the condition names from the colour key, with spaces turned
|
|
184
|
+
into underscores. An ontology id written in brackets after a name, as
|
|
185
|
+
studio2ga4gh reads it ("Breast cancer [MONDO:0007254]"), is left out of the
|
|
186
|
+
column name, which is then `Breast_cancer`, and a note says which id it had.
|
|
187
|
+
Only the ids of ontologies studio2ga4gh can declare count (the HPO, Mondo,
|
|
188
|
+
OMIM, Orphanet, SNOMED CT and so on): anything else in brackets, such as
|
|
189
|
+
"Hypertension (BP:160/95)", stays part of the name.
|
|
190
|
+
If two conditions would give the same column name, the second becomes
|
|
191
|
+
`Breast_cancer_2`.
|
|
192
|
+
|
|
193
|
+
## Phenotype detection
|
|
194
|
+
|
|
195
|
+
The converter maps Pedigree Studio's markings to phenotype status using the
|
|
196
|
+
colour key:
|
|
197
|
+
|
|
198
|
+
| Marking | Colour key entry | Status |
|
|
199
|
+
|---------------|-------------------|--------|
|
|
200
|
+
| Solid colour fill | `#rrggbb` | Affected (2) |
|
|
201
|
+
| Full shading (stripes/dots) | `shading:pattern:full` | Affected (2) |
|
|
202
|
+
| Half colour fill (one side only) | `#rrggbb` | Carrier (3) |
|
|
203
|
+
| Half shading | `shading:pattern:half-left` | Carrier (3) |
|
|
204
|
+
| Thirds or quarters (any part) | `#rrggbb` | Affected (2) |
|
|
205
|
+
| Centre dot (●) | — | Carrier (3) |
|
|
206
|
+
| No fill | — | Unaffected (1) |
|
|
207
|
+
|
|
208
|
+
Phenotype names are the text typed into the colour key (for example "Breast
|
|
209
|
+
cancer"). A colour with no text in the key is ignored.
|
|
210
|
+
|
|
211
|
+
A centre dot gives 3 in the Carrier column, and in the column of the condition
|
|
212
|
+
the person is drawn with; on a symbol with no fill it is the first condition
|
|
213
|
+
in the key.
|
|
214
|
+
|
|
215
|
+
## Multiple pedigrees
|
|
216
|
+
|
|
217
|
+
If a drawing holds several unconnected families (common when several are
|
|
218
|
+
drawn on the same canvas), each is written to a file of its own:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
okafor_1.mped # the first family
|
|
222
|
+
okafor_2.mped # the second family
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The colour key belongs to the whole drawing, so every family is written in the
|
|
226
|
+
same format: all `.ped` or all `.mped`.
|
|
227
|
+
|
|
228
|
+
## The standalone script
|
|
229
|
+
|
|
230
|
+
`studio2ped_standalone.py` is the whole package in one file, for computers
|
|
231
|
+
where installing a package is awkward; it takes the same options as the
|
|
232
|
+
`studio2ped` command. It is made from the package by
|
|
233
|
+
`python tools/make_standalone.py`, and a test checks that the two agree.
|
|
234
|
+
|
|
235
|
+
## Development
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
pip install -e ".[test]"
|
|
239
|
+
pytest
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Licence
|
|
243
|
+
|
|
244
|
+
MIT
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# studio2ped
|
|
2
|
+
|
|
3
|
+
Convert families drawn in [Pedigree Studio](https://github.com/comparativechrono/PedigreeStudio)
|
|
4
|
+
into standard PED pedigree files, or into extended MPED (multi-phenotype PED)
|
|
5
|
+
files when more than one condition is recorded.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install studio2ped
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
It needs Python 3.9 or later and nothing else. You can also use the
|
|
14
|
+
standalone script, `studio2ped_standalone.py`, without installing anything.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### Command line
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Basic conversion: writes okafor.ped, or okafor.mped if the family needs MPED
|
|
22
|
+
studio2ped okafor.studio.json
|
|
23
|
+
|
|
24
|
+
# Specify output name and directory
|
|
25
|
+
studio2ped okafor.studio.json -o my_pedigree -d ./output/
|
|
26
|
+
|
|
27
|
+
# Identify people by the labels the drawing shows, instead of pedigree numbers
|
|
28
|
+
studio2ped okafor.studio.json --ids drawing
|
|
29
|
+
|
|
30
|
+
# Using the standalone script
|
|
31
|
+
python studio2ped_standalone.py okafor.studio.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The command prints a short summary, the files it wrote and notes on anything
|
|
35
|
+
you should know: symbols that are not people and were left out, identifiers
|
|
36
|
+
that had to be changed, and so on.
|
|
37
|
+
|
|
38
|
+
Commands written for version 0.1.0 still work. The difference you will notice
|
|
39
|
+
is that people are now identified by their pedigree numbers; add `--ids text`
|
|
40
|
+
to get the identifiers 0.1.0 used (see [Identifying people](#identifying-people)).
|
|
41
|
+
|
|
42
|
+
### Python API
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import json
|
|
46
|
+
import studio2ped
|
|
47
|
+
|
|
48
|
+
with open("okafor.studio.json", encoding="utf-8") as f:
|
|
49
|
+
session = json.load(f)
|
|
50
|
+
|
|
51
|
+
result = studio2ped.convert(session, "okafor") # ids="drawing", "studio" or "text" to choose identifiers
|
|
52
|
+
print(result.summary)
|
|
53
|
+
for note in result.notes:
|
|
54
|
+
print("Note:", note)
|
|
55
|
+
for filename, content in result.files:
|
|
56
|
+
print(f"--- {filename} ---")
|
|
57
|
+
print(content)
|
|
58
|
+
|
|
59
|
+
# From file to file
|
|
60
|
+
result = studio2ped.convert_file("okafor.studio.json")
|
|
61
|
+
for filepath, _ in result.files:
|
|
62
|
+
print(f"Written: {filepath}")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## What it does
|
|
66
|
+
|
|
67
|
+
The converter reads a Pedigree Studio session file and:
|
|
68
|
+
|
|
69
|
+
1. **Detects separate pedigrees**: if the canvas holds several unconnected
|
|
70
|
+
families, each is written to a file of its own.
|
|
71
|
+
2. **Works out the family structure** from the partnership and child lines,
|
|
72
|
+
not from where the symbols are, so a rearranged pedigree converts correctly.
|
|
73
|
+
3. **Leaves out symbols that are not people.** Pedigree Studio stores the "no
|
|
74
|
+
children" bar and the "infertility" double bar as children of the couple;
|
|
75
|
+
they are not written as individuals, and a note names the couple.
|
|
76
|
+
4. **Gives genetic parents only.** The father and mother columns of a PED
|
|
77
|
+
file are genetic parents. A child joined to a couple by a dashed line (an
|
|
78
|
+
adoption) gets 0 for that couple, and so does the place of a parent marked
|
|
79
|
+
G (a gestational carrier), with a note in each case; a parent marked D (a
|
|
80
|
+
donor) is a genetic parent. Drawings made before the 2025 correction to the
|
|
81
|
+
pedigree nomenclature mark a gestational carrier S, which is read the same
|
|
82
|
+
way. studio2ga4gh's PED-style pedigree gives the same.
|
|
83
|
+
5. **Extracts phenotypes** from the colour key: solid fills, half fills,
|
|
84
|
+
symbols in thirds or quarters, and shading patterns are all recognised.
|
|
85
|
+
6. **Detects carrier notation**: centre dots (●) and half-filled symbols mark
|
|
86
|
+
carriers.
|
|
87
|
+
7. **Chooses the format**:
|
|
88
|
+
- **one phenotype** → a standard `.ped` file (6 columns);
|
|
89
|
+
- **several phenotypes**, or **carrier notation alongside a phenotype** → an
|
|
90
|
+
extended `.mped` file.
|
|
91
|
+
|
|
92
|
+
## Identifying people
|
|
93
|
+
|
|
94
|
+
| `--ids` | Each person is identified by |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `numbers` (the default) | their pedigree number as Pedigree Studio shows it: the generation in Roman numerals and the position from the left, such as `II-3` |
|
|
97
|
+
| `drawing` | the label the drawing shows under its numbering setting: pedigree numbers, letters (`II-C`, or `A`, `B`, `C` …) or labels typed in |
|
|
98
|
+
| `studio` | Pedigree Studio's own id, such as `p-3` |
|
|
99
|
+
| `text` | what version 0.1.0 used: the label typed in if there is one, otherwise the first line of the text under the symbol, otherwise Pedigree Studio's id |
|
|
100
|
+
|
|
101
|
+
Pedigree numbers are the ones studio2ga4gh writes into the GA4GH files, so a
|
|
102
|
+
person can be followed from one file to the other. The text under a symbol
|
|
103
|
+
usually holds a name and an age ("Alice, 50"), so `text`, and `drawing` when
|
|
104
|
+
the drawing has labels typed in, can put names into the file: use them only
|
|
105
|
+
when that is acceptable for wherever the file is going.
|
|
106
|
+
|
|
107
|
+
Identifiers can't contain spaces, so spaces become underscores and other
|
|
108
|
+
punctuation is dropped: "J. Mensah" becomes `J_Mensah`. studio2ga4gh makes its
|
|
109
|
+
ids from labels the same way, so with the same choice a person has the same
|
|
110
|
+
identifier in the PED file and in the GA4GH files. No two people may share an
|
|
111
|
+
identifier, and `0` means "no parent" in a PED file, so if a label would be
|
|
112
|
+
shared by two people (two cousins both labelled "26", say), would be empty or
|
|
113
|
+
would be `0`, the person is given their pedigree number instead, and a note
|
|
114
|
+
says so.
|
|
115
|
+
|
|
116
|
+
Pedigree numbers follow the drawing, as in Pedigree Studio: a new generation
|
|
117
|
+
starts 80 pixels or more below the first person of the generation above, and
|
|
118
|
+
each generation is numbered from the left. Only people are numbered: the "no
|
|
119
|
+
children" and "infertility" symbols are not people, so they have no number
|
|
120
|
+
and do not count in their row.
|
|
121
|
+
|
|
122
|
+
## Output formats
|
|
123
|
+
|
|
124
|
+
### Standard PED (`.ped`)
|
|
125
|
+
|
|
126
|
+
The standard 6-column format used by PLINK, GATK and most genetics tools:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
# Family: FAM1
|
|
130
|
+
# FamID IndID FatherID MotherID Sex Phenotype
|
|
131
|
+
FAM1 I-1 0 0 1 1
|
|
132
|
+
FAM1 I-2 0 0 2 1
|
|
133
|
+
FAM1 II-1 I-1 I-2 2 2
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected. The columns
|
|
137
|
+
are separated by tabs.
|
|
138
|
+
|
|
139
|
+
### Extended MPED (`.mped`)
|
|
140
|
+
|
|
141
|
+
A multi-phenotype extension for pedigrees in which more than one condition is
|
|
142
|
+
tracked. It adds a header line naming the phenotype columns, and a carrier
|
|
143
|
+
status:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
# MPED v1 Breast_cancer Carrier
|
|
147
|
+
# FamID IndID FatherID MotherID Sex Breast_cancer Carrier
|
|
148
|
+
FAM1 I-1 0 0 1 1 1
|
|
149
|
+
FAM1 I-2 0 0 2 3 3
|
|
150
|
+
FAM1 II-1 I-1 I-2 2 2 1
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected, `3` = carrier.
|
|
154
|
+
|
|
155
|
+
The format is easy to read by program: the first line declares the format and
|
|
156
|
+
names the phenotype columns, the second is a header for people to read, and
|
|
157
|
+
the data lines have the same tab-separated columns as standard PED, with one
|
|
158
|
+
more column for each phenotype.
|
|
159
|
+
|
|
160
|
+
Column names are the condition names from the colour key, with spaces turned
|
|
161
|
+
into underscores. An ontology id written in brackets after a name, as
|
|
162
|
+
studio2ga4gh reads it ("Breast cancer [MONDO:0007254]"), is left out of the
|
|
163
|
+
column name, which is then `Breast_cancer`, and a note says which id it had.
|
|
164
|
+
Only the ids of ontologies studio2ga4gh can declare count (the HPO, Mondo,
|
|
165
|
+
OMIM, Orphanet, SNOMED CT and so on): anything else in brackets, such as
|
|
166
|
+
"Hypertension (BP:160/95)", stays part of the name.
|
|
167
|
+
If two conditions would give the same column name, the second becomes
|
|
168
|
+
`Breast_cancer_2`.
|
|
169
|
+
|
|
170
|
+
## Phenotype detection
|
|
171
|
+
|
|
172
|
+
The converter maps Pedigree Studio's markings to phenotype status using the
|
|
173
|
+
colour key:
|
|
174
|
+
|
|
175
|
+
| Marking | Colour key entry | Status |
|
|
176
|
+
|---------------|-------------------|--------|
|
|
177
|
+
| Solid colour fill | `#rrggbb` | Affected (2) |
|
|
178
|
+
| Full shading (stripes/dots) | `shading:pattern:full` | Affected (2) |
|
|
179
|
+
| Half colour fill (one side only) | `#rrggbb` | Carrier (3) |
|
|
180
|
+
| Half shading | `shading:pattern:half-left` | Carrier (3) |
|
|
181
|
+
| Thirds or quarters (any part) | `#rrggbb` | Affected (2) |
|
|
182
|
+
| Centre dot (●) | — | Carrier (3) |
|
|
183
|
+
| No fill | — | Unaffected (1) |
|
|
184
|
+
|
|
185
|
+
Phenotype names are the text typed into the colour key (for example "Breast
|
|
186
|
+
cancer"). A colour with no text in the key is ignored.
|
|
187
|
+
|
|
188
|
+
A centre dot gives 3 in the Carrier column, and in the column of the condition
|
|
189
|
+
the person is drawn with; on a symbol with no fill it is the first condition
|
|
190
|
+
in the key.
|
|
191
|
+
|
|
192
|
+
## Multiple pedigrees
|
|
193
|
+
|
|
194
|
+
If a drawing holds several unconnected families (common when several are
|
|
195
|
+
drawn on the same canvas), each is written to a file of its own:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
okafor_1.mped # the first family
|
|
199
|
+
okafor_2.mped # the second family
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The colour key belongs to the whole drawing, so every family is written in the
|
|
203
|
+
same format: all `.ped` or all `.mped`.
|
|
204
|
+
|
|
205
|
+
## The standalone script
|
|
206
|
+
|
|
207
|
+
`studio2ped_standalone.py` is the whole package in one file, for computers
|
|
208
|
+
where installing a package is awkward; it takes the same options as the
|
|
209
|
+
`studio2ped` command. It is made from the package by
|
|
210
|
+
`python tools/make_standalone.py`, and a test checks that the two agree.
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
pip install -e ".[test]"
|
|
216
|
+
pytest
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Licence
|
|
220
|
+
|
|
221
|
+
MIT
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "studio2ped"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Convert Pedigree Studio session JSON to PED/MPED pedigree files"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
license =
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
11
12
|
requires-python = ">=3.9"
|
|
12
13
|
classifiers = [
|
|
13
14
|
"Development Status :: 4 - Beta",
|
|
@@ -15,25 +16,26 @@ classifiers = [
|
|
|
15
16
|
"Intended Audience :: Healthcare Industry",
|
|
16
17
|
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
17
18
|
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
18
|
-
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
|
-
"Programming Language :: Python :: 3.9",
|
|
21
|
-
"Programming Language :: Python :: 3.10",
|
|
22
|
-
"Programming Language :: Python :: 3.11",
|
|
23
|
-
"Programming Language :: Python :: 3.12",
|
|
24
|
-
"Programming Language :: Python :: 3.13",
|
|
25
20
|
]
|
|
26
21
|
keywords = ["pedigree", "genetics", "ped", "family-tree", "clinical-genetics", "pedigree-studio"]
|
|
27
22
|
authors = [
|
|
28
23
|
{name = "Tim Hearn", email = "tjh70@cam.ac.uk"},
|
|
29
24
|
]
|
|
30
25
|
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
test = ["pytest>=7"]
|
|
28
|
+
|
|
31
29
|
[project.scripts]
|
|
32
30
|
studio2ped = "studio2ped.cli:main"
|
|
33
31
|
|
|
34
32
|
[project.urls]
|
|
35
33
|
Homepage = "https://github.com/comparativechrono/studio2ped"
|
|
36
34
|
Issues = "https://github.com/comparativechrono/studio2ped/issues"
|
|
35
|
+
Changelog = "https://github.com/comparativechrono/studio2ped/blob/main/CHANGELOG.md"
|
|
37
36
|
|
|
38
37
|
[tool.setuptools.packages.find]
|
|
39
38
|
include = ["studio2ped*"]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""studio2ped — Convert Pedigree Studio session JSON to PED/MPED pedigree files."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.2.1"
|
|
4
|
+
|
|
5
|
+
from .converter import ( # noqa: E402
|
|
6
|
+
ID_CHOICES, ConversionResult, convert, convert_file, individual_ids, parse_session, pedigree_numbers,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = ["convert", "convert_file", "parse_session", "individual_ids", "pedigree_numbers", "ID_CHOICES",
|
|
10
|
+
"ConversionResult", "__version__"]
|
|
@@ -5,7 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
-
from .
|
|
8
|
+
from . import __version__
|
|
9
|
+
from .converter import ID_CHOICES, convert_file
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -21,7 +22,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
21
22
|
"-o", "--output-prefix",
|
|
22
23
|
default=None,
|
|
23
24
|
dest="output_prefix",
|
|
24
|
-
help="Base name for output files (default: input
|
|
25
|
+
help="Base name for output files (default: the input file's name without .studio.json)",
|
|
25
26
|
)
|
|
26
27
|
parser.add_argument(
|
|
27
28
|
"-d", "--output-dir",
|
|
@@ -29,6 +30,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
29
30
|
dest="output_dir",
|
|
30
31
|
help="Directory for output files (default: same as input file)",
|
|
31
32
|
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--ids",
|
|
35
|
+
choices=ID_CHOICES,
|
|
36
|
+
default="numbers",
|
|
37
|
+
help="How people are identified: pedigree numbers such as II-3 (numbers, the default), the labels "
|
|
38
|
+
"the drawing shows (drawing), Pedigree Studio's own ids (studio), or version 0.1.0's "
|
|
39
|
+
"identifiers taken from the text under each symbol, which are often names (text)",
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument("--version", action="version", version=f"studio2ped {__version__}")
|
|
32
42
|
|
|
33
43
|
args = parser.parse_args(argv)
|
|
34
44
|
|
|
@@ -37,13 +47,16 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
37
47
|
args.input,
|
|
38
48
|
output_prefix=args.output_prefix,
|
|
39
49
|
output_dir=args.output_dir,
|
|
50
|
+
ids=args.ids,
|
|
40
51
|
)
|
|
41
52
|
print(result.summary)
|
|
42
53
|
for filepath, _ in result.files:
|
|
43
54
|
print(f" Written: {filepath}")
|
|
55
|
+
for note in result.notes:
|
|
56
|
+
print(f" Note: {note}")
|
|
44
57
|
return 0
|
|
45
|
-
except FileNotFoundError:
|
|
46
|
-
print(f"Error: file not found: {args.input}", file=sys.stderr)
|
|
58
|
+
except FileNotFoundError as e:
|
|
59
|
+
print(f"Error: file not found: {e.filename or args.input}", file=sys.stderr)
|
|
47
60
|
return 1
|
|
48
61
|
except Exception as e:
|
|
49
62
|
print(f"Error: {e}", file=sys.stderr)
|