Source code for bioarch.individual

#!/usr/bin/env python


import functools
from typing import Optional


import pandas as pd


from .age import EstimatedAge
from .context import Context
from .joints import Joints
from .left_right import LeftRight
from .mouth import Mouth
from .occupational_markers import OccupationalMarkers
from .sex import Sex
from .trauma import Trauma


# Notes:
#  * https://www.archaeologists.net/sites/default/files/ifa_paper_7.pdf


[docs]class BurialInfo(object): """docstring for BurialInfo""" def __init__(self, site_name: str, site_id: str): if site_name is None or site_id is None: raise ValueError('site_name and site_id required') if site_name == '' or site_id == '': raise ValueError('site_name and site_id required') self.name = site_name self.id = site_id
[docs] def to_pd_series(self, prefix=''): labels = [f'{prefix}{label}' for label in ['name', 'id']] return pd.Series([self.name, self.id], index=labels, copy=True)
[docs]@functools.total_ordering class LongBoneMeasurement(object): """docstring for LongBoneMeasurement""" def __init__(self, _max: Optional[float], bi: Optional[float], head: Optional[float], distal: Optional[float]): self.max = _max self.bi = bi self.head = head self.distal = distal
[docs] @staticmethod def empty(): return LongBoneMeasurement(None, None, None, None)
[docs] @staticmethod def empty_lr(): return LeftRight(LongBoneMeasurement.empty(), LongBoneMeasurement.empty())
[docs] def to_pd_series(self, prefix=''): labels = [f'{prefix}{label}' for label in ['max', 'bi', 'head', 'distal']] return pd.Series([self.max, self.bi, self.head, self.distal], index=labels, copy=True)
def __eq__(self, other): if type(other) != type(self): # pylint: disable=C0123 raise NotImplementedError return ((self.max, self.bi, self.head, self.distal) == (other.max, other.bi, other.head, other.distal)) # pylint: disable=C0325 def __lt__(self, other): if type(other) != type(self): # pylint: disable=C0123 raise NotImplementedError return ((self.max, self.bi, self.head, self.distal) < (other.max, other.bi, other.head, other.distal)) # pylint: disable=C0325
[docs]class OsteologicalSex(object): """docstring for OsteologicalSex""" def __init__(self, pelvic: Optional[Sex], cranium: Optional[Sex], combined: Optional[Sex]): self.pelvic = pelvic self.cranium = cranium self.combined = combined
[docs] @staticmethod def empty(): return OsteologicalSex(None, None, None)
[docs] def to_pd_data_frame(self, index): d = { 'id': pd.Series([index]), } for l in ('pelvic', 'cranium', 'combined'): val = getattr(self, l) d[f'{l}_cat'] = pd.Series([val.name if val else None], copy=True, dtype=Sex.dtype()) # noqa: E241 d[f'{l}_val'] = pd.Series([val.value if val else None], copy=True, dtype='Int64') val_bin = val.as_bin() if val else None d[f'{l}_bin_cat'] = pd.Series([val_bin.name if val_bin else None], copy=True, dtype=Sex.dtype()) # noqa: E241 d[f'{l}_bin_val'] = pd.Series([val_bin.value if val_bin else None], copy=True, dtype='Int64') return pd.DataFrame.from_dict(d).set_index('id')
[docs]class AgeSexStature(object): """docstring for AgeSexStature""" __slots__ = ['osteological_sex', 'age', 'femur', 'humerus', 'tibia', 'stature', 'body_mass'] def __init__(self, osteological_sex: OsteologicalSex, age: EstimatedAge, femur: LongBoneMeasurement, humerus: LongBoneMeasurement, tibia: LongBoneMeasurement, stature: Optional[str], body_mass: Optional[str]): # Sex self.osteological_sex = osteological_sex # Age self.age = age # Long bones self.femur = femur self.humerus = humerus self.tibia = tibia # Other self.stature = stature self.body_mass = body_mass
[docs] @staticmethod def empty(): return AgeSexStature(OsteologicalSex.empty(), EstimatedAge.empty(), LongBoneMeasurement.empty_lr(), LongBoneMeasurement.empty_lr(), LongBoneMeasurement.empty_lr(), None, None)
[docs] def to_pd_data_frame(self, index): data = { 'id': pd.Series([index]), 'stature': pd.Series([self.stature]), 'body_mass': pd.Series([self.body_mass]), } df = pd.DataFrame.from_dict(data).set_index('id') s = pd.Series([]) for bone in ('femur', 'humerus', 'tibia'): lr_val = getattr(self, bone) s = s.append(lr_val.left.to_pd_series(prefix=f'{bone}_left_')) s = s.append(lr_val.right.to_pd_series(prefix=f'{bone}_right_')) s = s.append(lr_val.avg().to_pd_series(prefix=f'{bone}_avg_')) long_bones = pd.DataFrame.from_dict({index: s}, orient='index') age = self.age.to_pd_data_frame(index).add_prefix(f'age_').rename(columns={f'age_id': 'id'}) oss = self.osteological_sex.to_pd_data_frame(index).add_prefix(f'osteological_sex_').rename(columns={f'osteological_sex_id': 'id'}) return df.join(long_bones, on='id', how='outer') \ .join(age, on='id', how='outer') \ .join(oss, on='id', how='outer')
[docs]class Individual(object): """docstring for Individual""" def __init__(self, _id: str, site: BurialInfo, age_sex_stature: AgeSexStature, mouth: Mouth, occupational_markers: OccupationalMarkers, joints: Joints, trauma: Trauma, context: Context): self.id = _id self.site = site self.age_sex_stature = age_sex_stature self.mouth = mouth self.occupational_markers = occupational_markers self.joints = joints self.trauma = trauma self.context = context
[docs] def to_pd_data_frame(self): s = pd.Series([self.id], index=['id'], copy=True) s = s.append(self.site.to_pd_series(prefix='site_')) s = s.append(self.mouth.to_pd_series(prefix='mouth_')) joints_df = self.joints.to_pd_data_frame(self.id).add_prefix('joints_').rename(columns={f'joints_id': 'id'}) ass_df = self.age_sex_stature.to_pd_data_frame(self.id).add_prefix('ass_').rename(columns={f'ass_id': 'id'}) om_df = self.occupational_markers.to_pd_data_frame(self.id).add_prefix('om_').rename(columns={f'om_id': 'id'}) trauma_df = self.trauma.to_pd_data_frame(self.id).add_prefix('trauma_').rename(columns={f'trauma_id': 'id'}) context_df = self.context.to_pd_data_frame(self.id).add_prefix('context_').rename(columns={f'context_id': 'id'}) df = pd.DataFrame.from_dict({self.id: s}, orient='index') return df.join(joints_df, on='id', how='outer') \ .join(ass_df, on='id', how='outer') \ .join(om_df, on='id', how='outer') \ .join(trauma_df, on='id', how='outer') \ .join(context_df, on='id', how='outer')
if __name__ == "__main__": raise RuntimeError('No main available')