Skip to content

Instantly share code, notes, and snippets.

@mlund
Last active March 8, 2024 19:39
Show Gist options
  • Select an option

  • Save mlund/e8add748a9071b2ea983 to your computer and use it in GitHub Desktop.

Select an option

Save mlund/e8add748a9071b2ea983 to your computer and use it in GitHub Desktop.
Python module to load xmgrace plots into numpy arrays
#!/usr/bin/env python
# Copyright (c) 2015-2023 Mikael Lund
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Module use:
#
# from loadgrace import LoadGrace
# grace = LoadGrace("filename.agr")
# for (x, y), label, comment in grace:
# plt.plot(x, y, label=label)
#
# Command line use:
#
# $ loadgrace.py --plot filename.agr
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# 23.5─ β–žβ–ž mmAm β–Œ β”‚
# β”‚ β–žβ–ž mmAm (ideal) ▐ β”‚
# β”‚ β–žβ–ž hisactophilin β–Œ β”‚
# β”‚ β–žβ–ž hisactophilin (ideal) ▐ β”‚
# 19.6─ β–Œ β–Œ β”‚
# β”‚ ▐ β–š β”‚
# β”‚ β–ž ▐ β”‚
# β”‚ β–—β–˜ ▐ β”‚
# 15.7─ ▐ β–Œ β”‚
# β”‚ β–Œ β–Œ β”‚
# β”‚ β–Œ ▐ β”‚
# β”‚ ▐ ▐ β–„β–„β–„ β”‚
# 11.8─ ▐ β–Œ β–ž β–š β–„β–„ β”‚
# β”‚ β–Œ ▝▖ β–—β–€ β–šβ–– β–—β–ž β–š β”‚
# β”‚ β–Œ β–Œ β–ž β–β–„β–„β–˜ ▝▖ β”‚
# β”‚ ▐ ▐ ▐ ▝▖ β”‚
# β”‚ β–ž ▝▖ β–—β–˜ β–š β”‚
# 7.8─ β–„β–„β–„β–„β–„β–„β–šβ–„β–„β–– β–—β–˜ β–š β–„β–žβ–€β–– β–Œ β–β––β–„β–žβ–„β–„β–„ β”‚
# β”‚ β–„β–€ β–β–€β–€β–žβ–„β–– ▝▖ ▐ β–β–š ▐ β–—β–žβ–€β–€β–˜β–Œ β–€β–€β–€β”‚
# β”‚ β–—β–žβ–€ β–—β–˜ β–β–€β–šβ–„β–– β–β–—β–˜ β–Œ β–—β–˜ β–„β–žβ–˜ ▝▖ β”‚
# β”‚β–€β–˜ ▐ β–β–€β–€β–šβ–„β–„β–– β–—β–˜β–„β–– ▝▄ β–ž β–—β–„β–€ ▝▖ β”‚
# 3.9─ β–—β–˜ β–—β–„ β–β–€β–„β–˜β–– β–β–€β–šβ–– ▝▖ β–ž β–—β–„β–€β–˜ β–š β”‚
# β”‚ β–—β–ž β–—β–žβ–€β–˜β–„β–€β–€β–€β–€β–€β–€β–€β–€β–€β–šβ–„β–„β––β–šβ–– ▝▖ β–ž β–—β–„β–„β–„ β–Œ β”‚
# β”‚ β–„β–„β–šβ–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–—β–žβ–˜β–€ β–β–€β–€β–€β–„β–β–„β––β–„β–„β–„β–—β–žβ–˜β–„β–„β–„β–„β–—β–„β–€β–€ β–€β–„β–„ ▝▄▖ β”‚
# β”‚β–„β–„β–€β–€ β–—β–„β–˜ β–—β–„β–žβ–˜ β–€β–€β–β–šβ–„β––β–„β–„β–„β–„β–„β–žβ–€β–˜β–„β–„β–„β–„β–„β–„β–„β–„β–žβ–€β–„β–„β––β–€β–€β–„β–„β–„β–„β–„β–„ ▝▄ β”‚
# 0.0β”€β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–„β–žβ–€β–€β–˜ ▝▀▀▀ ▝▀▀▀▀▀▄▄▄▄▄▄▄▄▄│
# β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
# 0.0 3.5 7.0 10.5 14.0
#
import numpy as np
import re
import sys
class LoadGrace:
"""
Simple module to load a Grace file (.agr) plot and extract
legends, comments, and extract data into numpy arrays.
"""
def __init__(self, filename):
self._sets = []
self._legends = []
self._comments = []
with open(filename) as file:
for line in file:
if re.compile(r"@\s+s(.*) legend", re.IGNORECASE).search(line):
self._legends.append(line.split('"')[1])
if re.compile(r"@\s+s(.*) comment", re.IGNORECASE).search(line):
self._comments.append(line.split('"')[1])
if "@target" in line:
values = []
next(file)
for line in file:
if line != "&\n":
values.append(np.fromstring(line, sep=" ", count=2))
else:
self._sets.append(np.array(values))
break
def sets(self):
"""Data sets"""
return self._sets
def legends(self):
"""Legends for each dataset"""
return self._legends
def comments(self):
"""Comments for each dataset"""
return self._comments
def __len__(self):
"""Number of datasets"""
return len(self._sets)
def __getitem__(self, key):
"""Get all data by index and enable iteration
Returns x, y, legend, comment
"""
x = self._sets[key][:, 0]
y = self._sets[key][:, 1]
return (x, y), self._legends[key], self._comments[key]
def plot(self):
"""Plot data using plotext"""
try:
import plotext as plt
except ImportError:
print(
"Install plotext with e.g. `pip install plotext` to plot data in the terminal"
)
sys.exit(1)
for dataset, legend, comment in self:
plt.plot(dataset[0], dataset[1], label=legend)
plt.show()
# If run as main program
if __name__ == "__main__":
import argparse
ps = argparse.ArgumentParser(description="Extract data from xmgrace (.agr) files")
ps.add_argument("filename", type=str, help="xmgrace file")
ps.add_argument("-v", "--version", action="version", version="%(prog)s 0.1.0")
ps.add_argument(
"-p",
"--plot",
action="store_true",
help="Plot data in terminal (requires plotext)",
)
args = ps.parse_args()
grace = LoadGrace(args.filename)
if args.plot:
grace.plot()
else:
for dataset, legend, comment in grace:
print(f"# Legend: {legend} Comment: {comment}")
print(dataset[0], dataset[1])
print()
sys.exit(0)
@mlund
Copy link
Author

mlund commented Dec 13, 2023

Sure, like so? I haven't used this for a very long time and there's certainly room for improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment