@yeyuan98/opencode-bioresearcher-plugin 1.6.5 → 1.6.7
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.
- package/README.md +1 -0
- package/dist/skills/pzfx-io/SKILL.md +111 -0
- package/dist/skills/pzfx-io/guides/convert.md +102 -0
- package/dist/skills/pzfx-io/guides/read-parse.md +116 -0
- package/dist/skills/pzfx-io/guides/schema-reference.md +167 -0
- package/dist/skills/pzfx-io/guides/write-edit.md +150 -0
- package/dist/skills/pzfx-io/pyproject.toml +6 -0
- package/dist/skills/pzfx-io/pzfx.py +1156 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,6 +137,7 @@ Plugin-shipped skills are automatically copied into `.opencode/skills/` at plugi
|
|
|
137
137
|
- `bioresearcher-core`: core patterns and utilities (retry, JSON tools, subagent waves) for skill development.
|
|
138
138
|
- `env-jsonc-setup`: guided setup for database connection configuration (db-tools).
|
|
139
139
|
- `gromacs-guides`: reusable guides for GROMACS molecular dynamics workflows.
|
|
140
|
+
- `pzfx-io`: read, parse, write, and convert GraphPad Prism PZFX files (inspect, edit, add-column, CSV/TSV/JSON round-trips).
|
|
140
141
|
- `bioresearcher-tests`: comprehensive test suite for plugin tools and skills.
|
|
141
142
|
|
|
142
143
|
Prompt the following and follow along:
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pzfx-io
|
|
3
|
+
description: Read, parse, write, and convert GraphPad Prism PZFX files
|
|
4
|
+
allowedTools:
|
|
5
|
+
- Read
|
|
6
|
+
- Bash
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# PZFX I/O
|
|
10
|
+
|
|
11
|
+
This skill provides tools for working with GraphPad Prism PZFX files.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### Step 1: Load This Skill
|
|
16
|
+
The skill is loaded automatically when agent calls `skill pzfx-io`.
|
|
17
|
+
|
|
18
|
+
### Step 2: Extract Skill Path
|
|
19
|
+
From the `<skill_files>` section in the skill tool output, extract the `<skill_path>` value.
|
|
20
|
+
|
|
21
|
+
### Step 3: Read the Relevant Guide
|
|
22
|
+
```
|
|
23
|
+
Read <skill_path>/guides/schema-reference.md
|
|
24
|
+
Read <skill_path>/guides/read-parse.md
|
|
25
|
+
Read <skill_path>/guides/write-edit.md
|
|
26
|
+
Read <skill_path>/guides/convert.md
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Available Commands
|
|
30
|
+
|
|
31
|
+
All commands are invoked via:
|
|
32
|
+
```bash
|
|
33
|
+
uv run python <skill_path>/pzfx.py <command> [options]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Command | Purpose |
|
|
37
|
+
|---------|---------|
|
|
38
|
+
| `inspect` | Show file metadata (tables, columns, types) |
|
|
39
|
+
| `read` | Extract table data to stdout (JSON/CSV/TSV) |
|
|
40
|
+
| `write` | Create new PZFX file from CSV/TSV/JSON data |
|
|
41
|
+
| `edit` | Modify cell values in an existing PZFX file |
|
|
42
|
+
| `add-column` | Add a new column to an existing table |
|
|
43
|
+
| `convert` | Convert PZFX to CSV/TSV/JSON files |
|
|
44
|
+
|
|
45
|
+
## Available Guides
|
|
46
|
+
|
|
47
|
+
| Task | Guide |
|
|
48
|
+
|------|-------|
|
|
49
|
+
| PZFX XML schema details | `guides/schema-reference.md` |
|
|
50
|
+
| Reading and parsing files | `guides/read-parse.md` |
|
|
51
|
+
| Writing and editing files | `guides/write-edit.md` |
|
|
52
|
+
| Converting to other formats | `guides/convert.md` |
|
|
53
|
+
|
|
54
|
+
## Guide Summaries
|
|
55
|
+
|
|
56
|
+
### schema-reference.md
|
|
57
|
+
Complete PZFX XML schema documentation:
|
|
58
|
+
- File structure and XML style variants (Prism 6-7 vs 8+)
|
|
59
|
+
- Table/Column element attributes and valid values
|
|
60
|
+
- Cell data formats, excluded values, comma decimals
|
|
61
|
+
- Validation rules per table type
|
|
62
|
+
|
|
63
|
+
### read-parse.md
|
|
64
|
+
How to inspect and read PZFX data:
|
|
65
|
+
- `inspect` for metadata overview
|
|
66
|
+
- `read` for data extraction (JSON, CSV, TSV)
|
|
67
|
+
- Excluded data handling modes (keep, exclude, star)
|
|
68
|
+
- Value normalization rules
|
|
69
|
+
- Multi-table and HugeTable handling
|
|
70
|
+
|
|
71
|
+
### write-edit.md
|
|
72
|
+
Creating and modifying PZFX files:
|
|
73
|
+
- `write` to create new files from CSV/TSV/JSON
|
|
74
|
+
- `edit` to modify cell values (with exclusion flags)
|
|
75
|
+
- `add-column` to append new columns
|
|
76
|
+
- Dry-run mode for validation
|
|
77
|
+
- Template preservation and round-trip safety
|
|
78
|
+
|
|
79
|
+
### convert.md
|
|
80
|
+
Converting PZFX to other formats:
|
|
81
|
+
- CSV, TSV, JSON output formats
|
|
82
|
+
- Single table or batch conversion
|
|
83
|
+
- Custom output paths
|
|
84
|
+
- Comparison with `read` command
|
|
85
|
+
|
|
86
|
+
## Common Workflows
|
|
87
|
+
|
|
88
|
+
### Inspect a file
|
|
89
|
+
```bash
|
|
90
|
+
uv run python <skill_path>/pzfx.py inspect <file.pzfx>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Read data as CSV
|
|
94
|
+
```bash
|
|
95
|
+
uv run python <skill_path>/pzfx.py read <file.pzfx> --format csv --include-x --include-row-titles
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Edit a column
|
|
99
|
+
```bash
|
|
100
|
+
uv run python <skill_path>/pzfx.py edit <file.pzfx> --column-title "Col" --subcolumn 0 --values '[1,2,3]'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Convert to CSV file
|
|
104
|
+
```bash
|
|
105
|
+
uv run python <skill_path>/pzfx.py convert <file.pzfx> --format csv
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Create new file from CSV
|
|
109
|
+
```bash
|
|
110
|
+
uv run python <skill_path>/pzfx.py write output.pzfx --data-file data.csv --data-format csv --table-type OneWay
|
|
111
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Converting PZFX Files
|
|
2
|
+
|
|
3
|
+
## Convert to CSV
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uv run python pzfx.py convert <file.pzfx> --format csv
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Creates `<basename>.csv` next to the original file.
|
|
10
|
+
|
|
11
|
+
## Convert to TSV
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv run python pzfx.py convert <file.pzfx> --format tsv
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Convert to JSON
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run python pzfx.py convert <file.pzfx> --format json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
JSON output includes full structured data:
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"success": true,
|
|
27
|
+
"table_id": "Table0",
|
|
28
|
+
"table_type": "OneWay",
|
|
29
|
+
"columns": [...],
|
|
30
|
+
"row_titles": [...],
|
|
31
|
+
"source_file": "/path/to/file.pzfx"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Convert Specific Table
|
|
36
|
+
|
|
37
|
+
For multi-table files, convert a single table:
|
|
38
|
+
```bash
|
|
39
|
+
uv run python pzfx.py convert <file.pzfx> --table 0 --format csv
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Convert All Tables
|
|
43
|
+
|
|
44
|
+
Without `--table`, all tables are converted. For CSV/TSV, each table gets a separate file:
|
|
45
|
+
```
|
|
46
|
+
basename_Table0.csv
|
|
47
|
+
basename_Table1.csv
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Custom Output Path
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv run python pzfx.py convert <file.pzfx> --format csv --output /path/to/output.csv
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Note: `--output` is only used when converting a single table (or the last table).
|
|
57
|
+
|
|
58
|
+
## Excluded Data Modes
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv run python pzfx.py convert <file.pzfx> --exclude-mode keep
|
|
62
|
+
uv run python pzfx.py convert <file.pzfx> --exclude-mode star
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Mode | Effect |
|
|
66
|
+
|------|--------|
|
|
67
|
+
| `exclude` (default) | Excluded values omitted (empty cells) |
|
|
68
|
+
| `keep` | Excluded values preserved as raw numbers |
|
|
69
|
+
| `star` | Excluded values marked with asterisk (e.g., `90*`) |
|
|
70
|
+
|
|
71
|
+
## Convert Output Includes
|
|
72
|
+
|
|
73
|
+
By default, converted output includes:
|
|
74
|
+
- Row titles (if present)
|
|
75
|
+
- X column data (if present, for XY/Survival tables)
|
|
76
|
+
- All Y column subcolumns with proper naming
|
|
77
|
+
|
|
78
|
+
Column naming follows YFormat conventions:
|
|
79
|
+
- Replicates: `Title_1`, `Title_2`
|
|
80
|
+
- SD: `Title_MEAN`, `Title_SD`
|
|
81
|
+
- SE: `Title_MEAN`, `Title_SE`
|
|
82
|
+
- etc.
|
|
83
|
+
|
|
84
|
+
## Batch Conversion Example
|
|
85
|
+
|
|
86
|
+
Convert all PZFX files in a directory:
|
|
87
|
+
```bash
|
|
88
|
+
for f in *.pzfx; do
|
|
89
|
+
uv run python pzfx.py convert "$f" --format csv
|
|
90
|
+
done
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Comparison with `read` Command
|
|
94
|
+
|
|
95
|
+
| Feature | `read` | `convert` |
|
|
96
|
+
|---------|--------|-----------|
|
|
97
|
+
| Output to stdout | Yes | No (writes file) |
|
|
98
|
+
| Write to file | No | Yes |
|
|
99
|
+
| Include row titles | `--include-row-titles` | Always included |
|
|
100
|
+
| Include X column | `--include-x` | Always included |
|
|
101
|
+
| Multi-table | One at a time (`--table`) | All or one |
|
|
102
|
+
| Use case | Quick inspection, piping | File export, batch processing |
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Reading and Parsing PZFX Files
|
|
2
|
+
|
|
3
|
+
## Quick Inspect
|
|
4
|
+
|
|
5
|
+
Get file metadata without extracting data:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv run python pzfx.py inspect <file.pzfx>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Output includes: table IDs, types, formats, row counts, column titles and subcolumn counts.
|
|
12
|
+
|
|
13
|
+
## Read Table Data
|
|
14
|
+
|
|
15
|
+
### JSON format (default)
|
|
16
|
+
```bash
|
|
17
|
+
uv run python pzfx.py read <file.pzfx> --format json --include-x --include-row-titles
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
JSON structure:
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"table_id": "Table0",
|
|
24
|
+
"table_type": "XY",
|
|
25
|
+
"columns": [
|
|
26
|
+
{
|
|
27
|
+
"title": "X Title",
|
|
28
|
+
"subcolumn_names": ["X Title"],
|
|
29
|
+
"data": [[1.0, 2.0, 3.0]]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"title": "Sample",
|
|
33
|
+
"subcolumn_names": ["Sample_1", "Sample_2"],
|
|
34
|
+
"data": [[100.0, 90.0, 80.0], [50.0, 60.0, 70.0]]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"row_titles": ["A", "B", "C"]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### CSV/TSV format
|
|
42
|
+
```bash
|
|
43
|
+
uv run python pzfx.py read <file.pzfx> --format csv --include-x --include-row-titles
|
|
44
|
+
uv run python pzfx.py read <file.pzfx> --format tsv --include-x
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Output:
|
|
48
|
+
```
|
|
49
|
+
ROWTITLE,XX,Ya_1,Ya_2,Yb_1,Yb_2
|
|
50
|
+
A,1.0,100.0,50.0,1.0,10.0
|
|
51
|
+
B,2.0,90.0,60.0,2.0,9.0
|
|
52
|
+
C,3.0,80.0,70.0,3.0,8.0
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Multi-Table Files
|
|
56
|
+
|
|
57
|
+
Specify table index (0-based):
|
|
58
|
+
```bash
|
|
59
|
+
uv run python pzfx.py read <file.pzfx> --table 1 --format csv
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use `inspect` first to see how many tables exist.
|
|
63
|
+
|
|
64
|
+
## Excluded Data Handling
|
|
65
|
+
|
|
66
|
+
Prism supports "excluding" (striking through) data points. Control with `--exclude-mode`:
|
|
67
|
+
|
|
68
|
+
| Mode | Effect | Example |
|
|
69
|
+
|------|--------|---------|
|
|
70
|
+
| `exclude` (default) | Excluded values → `null`/empty | `[100, null, 80]` |
|
|
71
|
+
| `keep` | Excluded values kept as-is | `[100, 90, 80]` |
|
|
72
|
+
| `star` | Append asterisk to excluded | `[100, "90*", 80]` |
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv run python pzfx.py read <file.pzfx> --exclude-mode star --format csv
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Value Normalization
|
|
79
|
+
|
|
80
|
+
Values are automatically normalized:
|
|
81
|
+
- `"42."` → integer `42`
|
|
82
|
+
- `"3.14"` → float `3.14`
|
|
83
|
+
- `"3,5"` → float `3.5` (comma-decimal notation)
|
|
84
|
+
- `""` or empty → `null`
|
|
85
|
+
- Non-numeric text → kept as string
|
|
86
|
+
|
|
87
|
+
## Reading Specific Table Types
|
|
88
|
+
|
|
89
|
+
### XY Tables (with X column)
|
|
90
|
+
```bash
|
|
91
|
+
uv run python pzfx.py read <file.pzfx> --include-x --format csv
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Contingency Tables (with row titles)
|
|
95
|
+
```bash
|
|
96
|
+
uv run python pzfx.py read <file.pzfx> --include-row-titles --format csv
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Date-Formatted X Columns
|
|
100
|
+
XColumn stores fractional years; XAdvancedColumn stores formatted date strings. Both are included with `--include-x`.
|
|
101
|
+
|
|
102
|
+
### HugeTable Files
|
|
103
|
+
Handled identically to regular Tables. `inspect` shows `"is_huge": true`.
|
|
104
|
+
|
|
105
|
+
## Error Output Format
|
|
106
|
+
|
|
107
|
+
On failure, JSON error is returned:
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"success": false,
|
|
111
|
+
"error": "CATEGORY: message",
|
|
112
|
+
"hint": "Suggested fix"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Error categories: `FILE_NOT_FOUND`, `PARSE_ERROR`, `VALIDATION_ERROR`
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# PZFX Schema Reference
|
|
2
|
+
|
|
3
|
+
## File Structure
|
|
4
|
+
|
|
5
|
+
Every PZFX file follows this layout:
|
|
6
|
+
|
|
7
|
+
```xml
|
|
8
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
9
|
+
<GraphPadPrismFile PrismXMLVersion="5.00">
|
|
10
|
+
<Created>...</Created>
|
|
11
|
+
<InfoSequence><Ref ID="Info0" Selected="1"></Ref></InfoSequence>
|
|
12
|
+
<Info ID="Info0">...</Info>
|
|
13
|
+
<TableSequence><Ref ID="Table0" Selected="1"></Ref></TableSequence>
|
|
14
|
+
<Table ...>...</Table>
|
|
15
|
+
<!--Analyses, graphs and layouts as compressed binary. Don't edit this part of the file.-->
|
|
16
|
+
<Template dt:dt="bin.base64" xmlns:dt="urn:schemas-microsoft-com:datatypes">base64...</Template>
|
|
17
|
+
</GraphPadPrismFile>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The `<Template>` block is an opaque base64-encoded zlib blob containing graphs, analyses, and layouts. It must never be modified.
|
|
21
|
+
|
|
22
|
+
## XML Style Variants
|
|
23
|
+
|
|
24
|
+
| Variant | Prism Version | Features |
|
|
25
|
+
|---------|---------------|----------|
|
|
26
|
+
| Classic | 6-7 | Closing tags (`</Subcolumn></YColumn>`), no xmlns |
|
|
27
|
+
| Modern | 8+ | Self-closing tags (`<Subcolumn/>`), `xmlns="http://graphpad.com/prism/Prism.htm"` on root |
|
|
28
|
+
|
|
29
|
+
Both variants are handled by stripping XML namespaces at parse time.
|
|
30
|
+
|
|
31
|
+
## Table Element Attributes
|
|
32
|
+
|
|
33
|
+
```xml
|
|
34
|
+
<Table ID="Table0" XFormat="numbers" YFormat="replicates" Replicates="2"
|
|
35
|
+
TableType="XY" EVFormat="AsteriskAfterNumber">
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Attribute | Required | Values |
|
|
39
|
+
|-----------|----------|--------|
|
|
40
|
+
| ID | Yes | Unique table identifier (e.g. "Table0") |
|
|
41
|
+
| TableType | Yes | `OneWay`, `TwoWay`, `XY`, `Survival`, `Contingency`, `PartsOfWhole` |
|
|
42
|
+
| XFormat | Yes | `none`, `numbers`, `date`, `error` |
|
|
43
|
+
| YFormat | Conditional | `replicates`, `SD`, `SE`, `CV`, `SDN`, `SEN`, `CVN`, `low-high`, `upper-lower-limits` |
|
|
44
|
+
| Replicates | Conditional | Integer, required when YFormat="replicates" |
|
|
45
|
+
| EVFormat | Yes | Always `AsteriskAfterNumber` |
|
|
46
|
+
|
|
47
|
+
### TableType to XFormat Mapping
|
|
48
|
+
|
|
49
|
+
| TableType | Required XFormat |
|
|
50
|
+
|-----------|-----------------|
|
|
51
|
+
| OneWay | none |
|
|
52
|
+
| TwoWay | none |
|
|
53
|
+
| Contingency | none |
|
|
54
|
+
| PartsOfWhole | none |
|
|
55
|
+
| XY | numbers (or date, error) |
|
|
56
|
+
| Survival | numbers |
|
|
57
|
+
|
|
58
|
+
## Column Types
|
|
59
|
+
|
|
60
|
+
### XColumn (XY/Survival tables only)
|
|
61
|
+
|
|
62
|
+
```xml
|
|
63
|
+
<XColumn Width="199" Subcolumns="1" Decimals="8">
|
|
64
|
+
<Title>X Title</Title>
|
|
65
|
+
<Subcolumn>
|
|
66
|
+
<d>1.0</d>
|
|
67
|
+
<d>2.0</d>
|
|
68
|
+
</Subcolumn>
|
|
69
|
+
</XColumn>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- When XFormat="error", has 2 subcolumns (value ± error)
|
|
73
|
+
- Subcolumns count is always 1 for numbers/date formats
|
|
74
|
+
|
|
75
|
+
### XAdvancedColumn (Date-formatted X columns)
|
|
76
|
+
|
|
77
|
+
```xml
|
|
78
|
+
<XAdvancedColumn Version="1" Width="199" Decimals="8" Subcolumns="1">
|
|
79
|
+
<Title>Date X</Title>
|
|
80
|
+
<Subcolumn>
|
|
81
|
+
<d>2023-01-15</d>
|
|
82
|
+
</Subcolumn>
|
|
83
|
+
</XAdvancedColumn>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- Stores formatted date strings alongside XColumn fractional years
|
|
87
|
+
- Always paired with an XColumn when XFormat="date"
|
|
88
|
+
|
|
89
|
+
### YColumn
|
|
90
|
+
|
|
91
|
+
```xml
|
|
92
|
+
<YColumn Width="89" Decimals="0" Subcolumns="2">
|
|
93
|
+
<Title>Sample</Title>
|
|
94
|
+
<Subcolumn><d>1.</d><d>2.</d></Subcolumn>
|
|
95
|
+
<Subcolumn><d>3.</d><d>4.</d></Subcolumn>
|
|
96
|
+
</YColumn>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Subcolumn count depends on YFormat:
|
|
100
|
+
|
|
101
|
+
| YFormat | Subcolumns | Naming Convention |
|
|
102
|
+
|---------|------------|-------------------|
|
|
103
|
+
| replicates | = Replicates | `{Title}_1`, `{Title}_2`, ... |
|
|
104
|
+
| SD | 2 | `{Title}_MEAN`, `{Title}_SD` |
|
|
105
|
+
| SE | 2 | `{Title}_MEAN`, `{Title}_SE` |
|
|
106
|
+
| CV | 2 | `{Title}_MEAN`, `{Title}_CV` |
|
|
107
|
+
| SDN | 3 | `{Title}_MEAN`, `{Title}_SD`, `{Title}_N` |
|
|
108
|
+
| SEN | 3 | `{Title}_MEAN`, `{Title}_SEM`, `{Title}_N` |
|
|
109
|
+
| CVN | 3 | `{Title}_MEAN`, `{Title}_CV`, `{Title}_N` |
|
|
110
|
+
| low-high | 3 | `{Title}_MEAN`, `{Title}_PLUSERROR`, `{Title}_MINUSERROR` |
|
|
111
|
+
| upper-lower-limits | 3 | `{Title}_MEAN`, `{Title}_UPPERLIMIT`, `{Title}_LOWERLIMIT` |
|
|
112
|
+
|
|
113
|
+
### RowTitlesColumn (Contingency tables)
|
|
114
|
+
|
|
115
|
+
```xml
|
|
116
|
+
<RowTitlesColumn Width="81">
|
|
117
|
+
<Subcolumn>
|
|
118
|
+
<d>Male</d>
|
|
119
|
+
<d>Female</d>
|
|
120
|
+
</Subcolumn>
|
|
121
|
+
</RowTitlesColumn>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Cell Data (`<d>` Elements)
|
|
125
|
+
|
|
126
|
+
```xml
|
|
127
|
+
<d>42.5</d> <!-- Normal value -->
|
|
128
|
+
<d>3,5</d> <!-- Comma decimal (3.5) -->
|
|
129
|
+
<d Excluded="1">99.0</d> <!-- Excluded/strikethrough value -->
|
|
130
|
+
<d></d> <!-- Empty cell -->
|
|
131
|
+
<d><TextAlign>Label</TextAlign></d> <!-- Text cell (non-numeric) -->
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- Comma-as-decimal: values like `3,5` are normalized to `3.5`
|
|
135
|
+
- Excluded cells have `Excluded="1"` attribute
|
|
136
|
+
- Empty `<d></d>` or `<d/>` represents missing data
|
|
137
|
+
|
|
138
|
+
## Other Elements
|
|
139
|
+
|
|
140
|
+
### Info Block
|
|
141
|
+
```xml
|
|
142
|
+
<Info ID="Info0">
|
|
143
|
+
<Title>Project info 1</Title>
|
|
144
|
+
<Notes></Notes>
|
|
145
|
+
<Constant><Name>Experiment Date</Name><Value>2024-01-01</Value></Constant>
|
|
146
|
+
</Info>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### HugeTable
|
|
150
|
+
Identical to `Table` internally, just uses `<HugeTable>` tag for large datasets.
|
|
151
|
+
|
|
152
|
+
### FloatingNote
|
|
153
|
+
```xml
|
|
154
|
+
<FloatingNote ID="#0" Width="262" Height="137" Left="451" Top="33" Expanded="1">
|
|
155
|
+
<Title>Note</Title>
|
|
156
|
+
<Content>Some annotation text</Content>
|
|
157
|
+
</FloatingNote>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Validation Rules
|
|
161
|
+
|
|
162
|
+
1. PartsOfWhole tables must have exactly 1 YColumn
|
|
163
|
+
2. Contingency tables must have a RowTitlesColumn
|
|
164
|
+
3. YColumn titles must be unique within a table
|
|
165
|
+
4. SD/SE/CV format requires exactly 2 subcolumns per YColumn
|
|
166
|
+
5. SDN/SEN/CVN/low-high/upper-lower-limits requires exactly 3 subcolumns
|
|
167
|
+
6. XY tables require XFormat in (numbers, date, error)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Writing and Editing PZFX Files
|
|
2
|
+
|
|
3
|
+
## Creating New PZFX Files
|
|
4
|
+
|
|
5
|
+
### From CSV/TSV data
|
|
6
|
+
```bash
|
|
7
|
+
uv run python pzfx.py write output.pzfx \
|
|
8
|
+
--data-file data.csv \
|
|
9
|
+
--data-format csv \
|
|
10
|
+
--table-type OneWay
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### From TSV data
|
|
14
|
+
```bash
|
|
15
|
+
uv run python pzfx.py write output.pzfx \
|
|
16
|
+
--data-file data.tsv \
|
|
17
|
+
--data-format tsv \
|
|
18
|
+
--table-type XY \
|
|
19
|
+
--x-format numbers \
|
|
20
|
+
--y-format replicates \
|
|
21
|
+
--replicates 2
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### From JSON data
|
|
25
|
+
```bash
|
|
26
|
+
uv run python pzfx.py write output.pzfx \
|
|
27
|
+
--data-file data.json \
|
|
28
|
+
--data-format json \
|
|
29
|
+
--table-type TwoWay \
|
|
30
|
+
--y-format SD
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Input CSV/TSV Format
|
|
34
|
+
|
|
35
|
+
Headers determine column assignment. For XY tables, the first non-ROWTITLE column becomes the X column:
|
|
36
|
+
|
|
37
|
+
```csv
|
|
38
|
+
ROWTITLE,Xconc,Ya,Yb
|
|
39
|
+
A,1.0,100,50
|
|
40
|
+
B,2.0,90,60
|
|
41
|
+
C,3.0,80,70
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For non-XY tables (XFormat=none), all columns become YColumns:
|
|
45
|
+
|
|
46
|
+
```csv
|
|
47
|
+
Aa,Bb
|
|
48
|
+
1,100
|
|
49
|
+
2,90
|
|
50
|
+
3,80
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Dry Run (validate without writing)
|
|
54
|
+
```bash
|
|
55
|
+
uv run python pzfx.py write output.pzfx \
|
|
56
|
+
--data-file data.csv --data-format csv \
|
|
57
|
+
--table-type OneWay --dry-run
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Editing Existing Files
|
|
61
|
+
|
|
62
|
+
### Modify cell values
|
|
63
|
+
```bash
|
|
64
|
+
uv run python pzfx.py edit <file.pzfx> \
|
|
65
|
+
--column-title "Aa" \
|
|
66
|
+
--subcolumn 0 \
|
|
67
|
+
--values '[10,20,30,40]'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Edit with exclusion flags
|
|
71
|
+
```bash
|
|
72
|
+
uv run python pzfx.py edit <file.pzfx> \
|
|
73
|
+
--column-title "Sample" \
|
|
74
|
+
--subcolumn 0 \
|
|
75
|
+
--values '[100,90,80]' \
|
|
76
|
+
--exclude-values '[false,true,false]'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Edit and save to different file
|
|
80
|
+
```bash
|
|
81
|
+
uv run python pzfx.py edit <file.pzfx> \
|
|
82
|
+
--column-title "Aa" \
|
|
83
|
+
--subcolumn 0 \
|
|
84
|
+
--values '[10,20,30,40]' \
|
|
85
|
+
--output modified.pzfx
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Preview changes without writing
|
|
89
|
+
```bash
|
|
90
|
+
uv run python pzfx.py edit <file.pzfx> \
|
|
91
|
+
--column-title "Aa" \
|
|
92
|
+
--subcolumn 0 \
|
|
93
|
+
--values '[10,20,30,40]' \
|
|
94
|
+
--dry-run
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Adding Columns
|
|
98
|
+
|
|
99
|
+
### Add a single-subcolumn column
|
|
100
|
+
```bash
|
|
101
|
+
uv run python pzfx.py add-column <file.pzfx> \
|
|
102
|
+
--title "NewCol" \
|
|
103
|
+
--subcolumns 1 \
|
|
104
|
+
--values '[[1,2,3,4]]' \
|
|
105
|
+
--output modified.pzfx
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Add a multi-subcolumn column (e.g., Mean ± SD)
|
|
109
|
+
```bash
|
|
110
|
+
uv run python pzfx.py add-column <file.pzfx> \
|
|
111
|
+
--title "Treatment" \
|
|
112
|
+
--subcolumns 2 \
|
|
113
|
+
--values '[[100,110,120],[10,20,30]]' \
|
|
114
|
+
--output modified.pzfx
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The `--values` is a JSON array of arrays: one inner array per subcolumn.
|
|
118
|
+
|
|
119
|
+
### Dry run
|
|
120
|
+
```bash
|
|
121
|
+
uv run python pzfx.py add-column <file.pzfx> \
|
|
122
|
+
--title "NewCol" --subcolumns 1 \
|
|
123
|
+
--values '[[1,2,3]]' --dry-run
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Table Type Constraints
|
|
127
|
+
|
|
128
|
+
| TableType | XFormat | YColumns | Special |
|
|
129
|
+
|-----------|---------|----------|---------|
|
|
130
|
+
| OneWay | none | >= 1 | Default column type |
|
|
131
|
+
| TwoWay | none | >= 1 | Supports error formats |
|
|
132
|
+
| XY | numbers/date/error | >= 1 | Requires XColumn |
|
|
133
|
+
| Survival | numbers | >= 1 | X = time, Y = event |
|
|
134
|
+
| Contingency | none | >= 1 | Requires RowTitlesColumn |
|
|
135
|
+
| PartsOfWhole | none | exactly 1 | Pie chart data |
|
|
136
|
+
|
|
137
|
+
## Template Preservation
|
|
138
|
+
|
|
139
|
+
All write/edit operations preserve the original `<Template>` base64 blob unchanged. This ensures graphs, analyses, and layouts survive round-trips.
|
|
140
|
+
|
|
141
|
+
## Round-Trip Safety
|
|
142
|
+
|
|
143
|
+
Read → edit → write preserves:
|
|
144
|
+
- All columns not being edited
|
|
145
|
+
- XColumn and XAdvancedColumn data
|
|
146
|
+
- RowTitlesColumn data
|
|
147
|
+
- FloatingNote annotations
|
|
148
|
+
- Table metadata (ID, type, format, replicates)
|
|
149
|
+
- The complete Template blob
|
|
150
|
+
- Info block data
|