Metadata#

Metadata can be added to TsGroup, IntervalSet, and TsdFrame objects at initialization or after an object has been created.

  • TsGroup metadata is information associated with each Ts/Tsd object, such as brain region or unit type.

  • IntervalSet metadata is information associated with each interval, such as a trial label or stimulus condition.

  • TsdFrame metadata is information associated with each column, such as a channel or position.

Adding metadata at initialization#

At initialization, metadata can be passed via a dictionary or pandas DataFrame using the keyword argument metadata. The metadata name is taken from the dictionary key or DataFrame column, and it can be set to any string name with a couple class-specific exceptions.

Class-specific exceptions

  • If column names are supplied to TsdFrame, metadata cannot overlap with those names.

  • The rate attribute for TsGroup is stored with the metadata and cannot be overwritten.

The length of the metadata must match the length of the object it describes (see class examples below for more detail).

Hide code cell content
import numpy as np
import pandas as pd
import pynapple as nap

# input parameters for TsGroup
group = {
    1: nap.Ts(t=np.sort(np.random.uniform(0, 100, 10))),
    2: nap.Ts(t=np.sort(np.random.uniform(0, 100, 20))),
    3: nap.Ts(t=np.sort(np.random.uniform(0, 100, 30))),
}

# input parameters for IntervalSet
starts = [0,10,20]
ends = [5,15,25]

# input parameters for TsdFrame
t = np.arange(5)
d = np.ones((5,3))
columns = ["a", "b", "c"]

TsGroup#

Metadata added to TsGroup must match the number of Ts/Tsd objects, or the length of its index property.

metadata = {"region": ["pfc", "ofc", "hpc"]}

tsgroup = nap.TsGroup(group, metadata=metadata)
print(tsgroup)
  Index     rate  region
-------  -------  --------
      1  0.10584  pfc
      2  0.21167  ofc
      3  0.31751  hpc

When initializing with a DataFrame, the index must align with the input dictionary keys (only when a dictionary is used to create the TsGroup).

metadata = pd.DataFrame(
    index=group.keys(),
    data=["pfc", "ofc", "hpc"],
    columns=["region"]
)

tsgroup = nap.TsGroup(group, metadata=metadata)
print(tsgroup)
  Index     rate  region
-------  -------  --------
      1  0.10584  pfc
      2  0.21167  ofc
      3  0.31751  hpc

IntervalSet#

Metadata added to IntervalSet must match the number of intervals, or the length of its index property.

metadata = {
    "reward": [1, 0, 1],
    "choice": ["left", "right", "left"],    
}
intervalset = nap.IntervalSet(starts, ends, metadata=metadata)
print(intervalset)
  index    start    end    reward  choice
      0        0      5         1  left
      1       10     15         0  right
      2       20     25         1  left
shape: (3, 2), time unit: sec.

Metadata can be initialized as a DataFrame using the metadata argument, or it can be inferred when initializing an IntervalSet with a DataFrame.

df = pd.DataFrame(
    data=[[0, 5, 1, "left"], [10, 15, 0, "right"], [20, 25, 1, "left"]], 
    columns=["start", "end", "reward", "choice"]
    )

intervalset = nap.IntervalSet(df)
print(intervalset)
  index    start    end    reward  choice
      0        0      5         1  left
      1       10     15         0  right
      2       20     25         1  left
shape: (3, 2), time unit: sec.

TsdFrame#

Metadata added to TsdFrame must match the number of data columns, or the length of its columns property.

metadata = {
    "color": ["red", "blue", "green"], 
    "position": [10,20,30]
    }

tsdframe = nap.TsdFrame(d=d, t=t, columns=["a", "b", "c"], metadata=metadata)
print(tsdframe)
Time (s)    a         b         c
----------  --------  --------  --------
0.0         1.0       1.0       1.0
1.0         1.0       1.0       1.0
2.0         1.0       1.0       1.0
3.0         1.0       1.0       1.0
4.0         1.0       1.0       1.0
Metadata
--------    --------  --------  --------
color       red       blue      green
position    10        20        30

dtype: float64, shape: (5, 3)

When initializing with a DataFrame, the DataFrame index must match the TsdFrame columns.

metadata = pd.DataFrame(
    index=["a", "b", "c"],
    data=[["red", 10], ["blue", 20], ["green", 30]], 
    columns=["color", "position"],
)

tsdframe = nap.TsdFrame(d=d, t=t, columns=["a", "b", "c"], metadata=metadata)
print(tsdframe)
Time (s)    a         b         c
----------  --------  --------  --------
0.0         1.0       1.0       1.0
1.0         1.0       1.0       1.0
2.0         1.0       1.0       1.0
3.0         1.0       1.0       1.0
4.0         1.0       1.0       1.0
Metadata
--------    --------  --------  --------
color       red       blue      green
position    10        20        30

dtype: float64, shape: (5, 3)

Adding metadata after initialization#

After creation, metadata can be added using the class method set_info(). Additionally, single metadata fields can be added as a dictionary-like key or as an attribute, with a few noted exceptions outlined below.

Note

The remaining metadata examples will be shown on a TsGroup object; however, all examples can be directly applied to IntervalSet and TsdFrame objects.

set_info#

Metadata can be passed as a dictionary or pandas DataFrame as the first positional argument, or metadata can be passed as name-value keyword arguments.

tsgroup.set_info(unit_type=["multi", "single", "single"])
print(tsgroup)
  Index     rate  region    unit_type
-------  -------  --------  -----------
      1  0.10584  pfc       multi
      2  0.21167  ofc       single
      3  0.31751  hpc       single

Using dictionary-like keys (square brackets)#

Most metadata names can set as a dictionary-like key (i.e. using square brackets). The only exceptions are for IntervalSet, where the names “start” and “end” are reserved for class properties.

tsgroup["depth"] = [0, 1, 2]
print(tsgroup)
  Index     rate  region    unit_type      depth
-------  -------  --------  -----------  -------
      1  0.10584  pfc       multi              0
      2  0.21167  ofc       single             1
      3  0.31751  hpc       single             2

Using attribute assignment#

If the metadata name is unique from other class attributes and methods, and it is formatted properly (i.e. only alpha-numeric characters and underscores), it can be set as an attribute (i.e. using a . followed by the metadata name).

tsgroup.label=["MUA", "good", "good"]
print(tsgroup)
  Index     rate  region    unit_type      depth  label
-------  -------  --------  -----------  -------  -------
      1  0.10584  pfc       multi              0  MUA
      2  0.21167  ofc       single             1  good
      3  0.31751  hpc       single             2  good

Accessing metadata#

Metadata is stored as a pandas DataFrame, which can be previewed using the metadata attribute.

print(tsgroup.metadata)
       rate region unit_type  depth label
1  0.105837    pfc     multi      0   MUA
2  0.211673    ofc    single      1  good
3  0.317510    hpc    single      2  good

Single metadata columns (or lists of columns) can be retrieved using the get_info() class method:

print(tsgroup.get_info("region"))
1    pfc
2    ofc
3    hpc
Name: region, dtype: object

Similarly, metadata can be accessed using key indexing (i.e. square brakets)

print(tsgroup["region"])
1    pfc
2    ofc
3    hpc
Name: region, dtype: object

Note

Metadata names must be strings. Key indexing with an integer will produce different behavior based on object type.

Finally, metadata that can be set as an attribute can also be accessed as an attribute.

print(tsgroup.region)
1    pfc
2    ofc
3    hpc
Name: region, dtype: object

Overwriting metadata#

User-set metadata is mutable and can be overwritten.

print(tsgroup, "\n")
tsgroup.set_info(region=["A", "B", "C"])
print(tsgroup)
  Index     rate  region    unit_type      depth  label
-------  -------  --------  -----------  -------  -------
      1  0.10584  pfc       multi              0  MUA
      2  0.21167  ofc       single             1  good
      3  0.31751  hpc       single             2  good 

  Index     rate  region    unit_type      depth  label
-------  -------  --------  -----------  -------  -------
      1  0.10584  A         multi              0  MUA
      2  0.21167  B         single             1  good
      3  0.31751  C         single             2  good

Allowed data types#

As long as the length of the metadata container matches the length of the object (number of columns for TsdFrame and number of indices for IntervalSet and TsGroup), elements of the metadata can be any data type.

tsgroup.coords = [[1,0],[0,1],[1,1]]
print(tsgroup.coords)
1    [1, 0]
2    [0, 1]
3    [1, 1]
Name: coords, dtype: object

Using metadata to slice objects#

Metadata can be used to slice or filter objects based on metadata values.

print(tsgroup[tsgroup.label == "good"])
  Index     rate  region    unit_type      depth  label    coords    ...
-------  -------  --------  -----------  -------  -------  --------  -----
      2  0.21167  B         single             1  good     [0, 1]    ...
      3  0.31751  C         single             2  good     [1, 1]    ...