Skip to content
88 changes: 39 additions & 49 deletions ubermagtable/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import ubermagutil.typesystem as ts
import ubermagutil.units

import ubermagtable.util as uu


@ts.typesystem(
data=ts.Typed(expected_type=pd.DataFrame), units=ts.Typed(expected_type=dict)
)
class Table:
"""Tabular data class.

Expand Down Expand Up @@ -57,59 +51,50 @@ def __init__(self, data, units, x=None, attributes=None):
self.x = x
self.attributes = attributes if attributes is not None else {}
self.attributes.setdefault("fourierspace", False)
# Detect duplicated lines as a last step to make use of the checks when
# assigning 'x' as independent variable.
self._duplicated_lines = any(self.data.duplicated(subset=self.x, keep="last"))
if self._duplicated_lines:
self.data.drop_duplicates(
subset=self.x,
keep="last",
inplace=True,
ignore_index=True, # reset the index to 0, 1, ..., n-1
)

@classmethod
def fromfile(cls, filename, /, x=None, rename=True):
"""Reads an OOMMF ``.odt`` or mumax3 ``.txt`` scalar data file and
returns a ``ubermagtable.Table`` object.

Parameters
----------
filename : str

OOMMF ``.odt`` or mumax3 ``.txt`` file.

x : str, optional

Independent variable name. Defaults to ``None``.

rename : bool, optional

If ``rename=True``, the column names are renamed with their shorter
versions. Defaults to ``True``.
@property
def data(self):
"""Scalar data of the drive.

Returns
-------
ubermagtable.Table

Table object.

Examples
--------
1. Defining ``ubermagtable.Table`` by reading an OOMMF ``.odt`` file.
pd.DataFrame
"""
return self._data

>>> import os
>>> import ubermagtable as ut
...
>>> odtfile = os.path.join(os.path.dirname(__file__),
... 'tests', 'test_sample',
... 'oommf-hysteresis1.odt')
>>> table = ut.Table.fromfile(odtfile, x='B_hysteresis')
@data.setter
def data(self, data):
if not isinstance(data, pd.DataFrame):
raise TypeError(f"Invalid {type(data)=}; expected 'pandas.DataFrame'.")
self._data = data

2. Defining ``ubermagtable.Table`` by reading a mumax3 ``.txt`` file.
@property
def units(self):
"""Units of the scalar data.

>>> odtfile = os.path.join(os.path.dirname(__file__),
... 'tests', 'test_sample', 'mumax3-file1.txt')
>>> table = ut.Table.fromfile(odtfile, x='t')
Returns
-------
dict

Keys are the columns in the ``data`` property, values the respective units.
"""
cols = uu.columns(filename, rename=rename)
return self._units

return cls(
data=pd.DataFrame(uu.data(filename), columns=cols),
units=uu.units(filename, rename=rename),
x=x,
)
@units.setter
def units(self, units):
if not isinstance(units, dict):
raise TypeError(f"Invalid {type(units)=}; 'expected dict'.")
self._units = units

@property
def x(self):
Expand Down Expand Up @@ -199,6 +184,11 @@ def xmax(self):
"""
return self.data[self.x].iloc[-1]

@property
def deduplicated(self):
"""Indicate if the table on disk contains duplicated steps."""
return self._duplicated_lines

def apply(self, func, columns=None, args=(), **kwargs):
r"""Apply function.

Expand Down
33 changes: 31 additions & 2 deletions ubermagtable/tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,34 @@ def test_init(self):
assert isinstance(table.data, pd.DataFrame)

def test_fromfile(self):
for odtfile in self.odtfiles:
table = ut.Table.fromfile(odtfile, rename=False)
check_table(table)

table_short_names = ut.Table.fromfile(odtfile, rename=True)
check_table(table_short_names)

assert len(table.data) == len(table_short_names.data)
assert len(table.data.columns) == len(table_short_names.data.columns)

def test_columns(self):
for odtfile in self.odtfiles:
for rename in [True, False]:
table = ut.Table.fromfile(odtfile)
check_table(table)
table = ut.Table.fromfile(odtfile, rename=rename)
columns = table.data.columns
assert all(isinstance(column, str) for column in columns)
assert len(columns) == len(set(columns)) # unique column names

def test_units(self):
for odtfile in self.odtfiles:
for rename in [True, False]:
table = ut.Table.fromfile(odtfile, rename=rename)
units = table.units
assert isinstance(units, dict)
assert all(isinstance(unit, str) for unit in units.keys())
assert all(isinstance(unit, str) for unit in units.values())
assert "J" in units.values() # Energy is always in
assert "" in units.values() # Columns with no units are always in

def test_xy(self):
table = ut.Table.fromfile(self.odtfiles[0], x="t")
Expand Down Expand Up @@ -178,6 +202,11 @@ def test_oommf_mel(self):
assert len(columns) == 16

def test_oommf_issue1(self):
"""The odt file contains columns ``Oxs_Exchange6Ngbr:...`` and
``My_Exchange6Ngbr:...``. During processing we remove the ``Oxs_`` or ``My_``
prefix and subsequently loose the "duplicated" columns in the pandas dataframe.

"""
table = ut.Table.fromfile(self.odtfiles[-1])
columns = table.data.columns.to_list()
assert len(columns) == 30
Expand Down
55 changes: 0 additions & 55 deletions ubermagtable/tests/test_util.py

This file was deleted.

2 changes: 0 additions & 2 deletions ubermagtable/util/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
"""Utility tools"""
from .util import columns, data, units
Loading