Basic Data Types

Mumodo Demo Notebook - Updated on 24.04.2015

Summary: This notebook describes the basic data types used by mumodo. In particular, the IntervalFrame, StreamFrame, as well as the InstantIO types are described.

(c) Dialogue Systems Group, University of Bielefeld


In [1]:
from mumodo.mumodoIO import open_intervalframe_from_textgrid, open_streamframe_from_xiofile, convert_pointtier_to_streamframe
from mumodo.analysis import convert_times_of_tier
from mumodo.InstantIO import *
import pandas as pd

Analysis in mumodo is based on only two generic types of objects. The StreamFrame and the IntervalFrame. Let's have a look at them.

The StreamFrame


In [2]:
mystreamframe = open_streamframe_from_xiofile("sampledata/test.xio.gz", 
                                              'VeniceHubReplay/Venice/Body1').ix[:, ['JointPositions3', 'JointPositions4']]


opening compressed file ...
opening file without indexing

A StreamFrame is a Pandas DataFrame object (See Pandas Documentation), that satisfies the following conditions

  • The index of the StreamFrame is a time axis (typically in milliseconds as integer numbers)
  • The column names of the StreamFrame represent objects that have values at those specific times

So, in essence, StreamFrames are multivariate time series.

The powerful Pandas functionality allows easy access to specific cells of the table, using the time and the name of the column e.g.


In [3]:
#so values of the first column in the first 1000 ms
print mystreamframe.columns
mystreamframe.ix[0:1000, ['JointPositions3']]


Index([u'JointPositions3', u'JointPositions4'], dtype='object')
Out[3]:
JointPositions3
0 [0.957876 -0.152858 1.7562, 0.945315 0.162776 ...
18 [0.957869 -0.152829 1.75661, 0.945076 0.162929...
52 [0.957912 -0.152832 1.7567, 0.94512 0.162897 1...
85 [0.957949 -0.152806 1.75688, 0.945135 0.162988...
118 [0.957971 -0.152806 1.75699, 0.945102 0.162988...
151 [0.958053 -0.152739 1.75721, 0.945159 0.163071...
185 [0.958094 -0.152707 1.75739, 0.94516 0.16309 1...
218 [0.958163 -0.152711 1.75756, 0.945215 0.163075...
252 [0.958274 -0.152644 1.75772, 0.945325 0.163101...
284 [0.958391 -0.152641 1.75794, 0.945421 0.163058...
318 [0.958553 -0.152569 1.75824, 0.945508 0.163044...
351 [0.958639 -0.152483 1.7584, 0.945538 0.163107 ...
385 [0.958754 -0.152495 1.75881, 0.945667 0.163019...
417 [0.95884 -0.152376 1.75907, 0.945764 0.163064 ...
451 [0.95892 -0.152272 1.75935, 0.945897 0.163253 ...
484 [0.959079 -0.152045 1.7597, 0.946018 0.163436 ...
518 [0.959204 -0.151983 1.76001, 0.94609 0.16356 1...
552 [0.959443 -0.15182 1.7604, 0.945945 0.163811 1...
585 [0.959574 -0.151726 1.7607, 0.946026 0.163902 ...
618 [0.959853 -0.15165 1.76125, 0.946239 0.164002 ...
652 [0.960079 -0.151612 1.76159, 0.946377 0.164147...
684 [0.960255 -0.151455 1.7618, 0.946458 0.164266 ...
717 [0.960455 -0.15139 1.76235, 0.946586 0.164278 ...
751 [0.960685 -0.151299 1.76267, 0.946709 0.164334...
784 [0.96077 -0.151272 1.76276, 0.946804 0.164323 ...
817 [0.961199 -0.151221 1.7632, 0.947132 0.164374 ...
851 [0.961467 -0.151137 1.76363, 0.947255 0.164508...
884 [0.961747 -0.15096 1.76423, 0.94766 0.164767 1...
917 [0.96206 -0.150904 1.76454, 0.94795 0.164934 1...
951 [0.96233 -0.150754 1.76483, 0.948115 0.165109 ...
984 [0.962696 -0.150661 1.76525, 0.948486 0.165374...

In [4]:
#print a specific value
x = mystreamframe['JointPositions4'][984]
print x


[-0.365828 -0.748412 0.920436, -0.369162 -0.484824 0.840081, -0.368622 -0.219277 0.743624, -0.431168 -0.0840901 0.74596, -0.460954 -0.203418 0.746415, -0.536243 -0.446039 0.79702, -0.392797 -0.30233 0.553855, -0.404106 -0.205538 0.639367, -0.280834 -0.299137 0.809516, -0.405373 -0.147385 0.548891, -0.441266 0.0828351 0.563569, -0.455148 0.158502 0.571112, -0.462402 -0.71796 0.918329, -0.384065 -1.01339 0.998303, -0.429193 -0.605885 0.786384, -0.324172 -0.56888 0.811714, -0.249249 -0.735671 0.869954, -0.0554025 -0.4619 1.00468, -0.307495 -0.476967 0.77663, -0.357563 -0.573694 0.686901, -0.369484 -0.285533 0.770414, -0.418141 -0.153545 0.620289, -0.414258 -0.180974 0.59362, -0.460152 0.192906 0.541056, -0.440446 0.153279 0.552278]

In [5]:
#print this value's type, these types are explained below
print type(x)


<class 'mumodo.InstantIO.MFVec3f'>

The IntervalFrame


In [6]:
myintervalframe = open_intervalframe_from_textgrid("sampledata/test.TextGrid")['S']

An IntervalFrame is a Pandas DataFrame object (See Pandas Documentation), that satisfies the following conditions

  • There are three columns, named 'start_time', 'end_time', and 'text', and are in that order
  • The time columns typically are float numbers representing seconds
  • The text column is typically of type string, although this is not required

So, in essence, IntervalFrames are annotation tiers commonly found in audio, video and other analysis software (e.g. Praat, ELAN)

The advantages of Pandas also apply to this category of objects, e.g.


In [7]:
myintervalframe


Out[7]:
start_time end_time text
0 1.30 1.860000 Hello
1 2.88 3.500000 I 'm Spyros
2 4.86 8.280000 Here in the Dialogue Systems Group, in the Uni...
3 8.50 10.400000 We have developed Mumodo, and Venice
4 11.58 11.840000 <CLAP>
5 14.10 17.220000 Well, right now we are being recorded by a cam...
6 17.54 18.840314 and a Microsoft Kinect sensor
7 19.30 21.100000 But how will we get the data from Kinect?
8 27.70 31.480000 We are using this timecode to synchronize the ...
9 31.62 32.860000 With the audio and video
10 33.76 34.000000 <CLAP>
11 37.40 41.300000 We can process the data that comes from Venice...
12 47.60 47.820000 <CLAP>
13 48.94 49.660000 Goodbye

The PointFrame


In [8]:
mypointframe = open_intervalframe_from_textgrid("sampledata/test.TextGrid")['CLAPS']

A PointFrame is a Pandas DataFrame object (See Pandas Documentation), that satisfies the following conditions

  • There are two columns, named 'time', and 'mark', and are in that order
  • The time column typically has float numbers representing seconds
  • The mark column is typically of type string, although this is not required

PointFrames are very similar to StreamFrames, but sometimes they need to be imported directly from Praat Textgrids (Praat Point Tiers) and are thus more like annotations without duration, rather than raw tracking data.

The advantages of Pandas also apply to this category of objects, e.g.


In [9]:
mypointframe


Out[9]:
time mark
0 11.654230 First Clap
1 33.824485 Second Clap
2 47.672685 Third Clap

It is possible to convert the point_tier to a streamframe (the units are not converted) using a specially designed function


In [10]:
#first convert the units to ms
convert_times_of_tier(mypointframe, lambda x: int(1000 * x))
#next, convert to a streamframe
mynewstreamframe = convert_pointtier_to_streamframe(mypointframe)
mynewstreamframe


Out[10]:
mark
11654 First Clap
33824 Second Clap
47672 Third Clap

The InstantIO Types

Multimodal data typically consists of audio, video and other sensors, such as motion capture, eye tracking, etc.

In order to deal with this complex data, mumodo supports a number of types inspired by the InstantIO framework for Virtual Reality (www.instantreality.org). The following types are supported

  • SFFloat, SFInt32, SFString, SFBool - Primitive types for float, int, string and boolean values, respectively
  • SFVec2f, SFVec3f, SFRotation - Geometry types for 2D or 3D vectors of floats, as well as quaternions representing rotations
  • MFVec3f, MFFloat, MFRotation, ... - Multi-Field versions of the types mentioned above

So let's have a look at the value x that we got from our table above ...


In [11]:
type(x)


Out[11]:
mumodo.InstantIO.MFVec3f

It is a multi-field SFVec3f. Is that really so?


In [12]:
type(x[0])


Out[12]:
mumodo.InstantIO.SFVec3f

Indeed! MF types also have length and are iterable


In [13]:
for y in x:
    print y


-0.365828 -0.748412 0.920436
-0.369162 -0.484824 0.840081
-0.368622 -0.219277 0.743624
-0.431168 -0.0840901 0.74596
-0.460954 -0.203418 0.746415
-0.536243 -0.446039 0.79702
-0.392797 -0.30233 0.553855
-0.404106 -0.205538 0.639367
-0.280834 -0.299137 0.809516
-0.405373 -0.147385 0.548891
-0.441266 0.0828351 0.563569
-0.455148 0.158502 0.571112
-0.462402 -0.71796 0.918329
-0.384065 -1.01339 0.998303
-0.429193 -0.605885 0.786384
-0.324172 -0.56888 0.811714
-0.249249 -0.735671 0.869954
-0.0554025 -0.4619 1.00468
-0.307495 -0.476967 0.77663
-0.357563 -0.573694 0.686901
-0.369484 -0.285533 0.770414
-0.418141 -0.153545 0.620289
-0.414258 -0.180974 0.59362
-0.460152 0.192906 0.541056
-0.440446 0.153279 0.552278

In [14]:
len(x)


Out[14]:
25

The basic (SF) types have some useful atttibutes, e.g.


In [15]:
my3Dvector = x[0]
print type(my3Dvector)
print my3Dvector.x, my3Dvector.y, my3Dvector.z #per axis x, y, z access
print my3Dvector.v #get it as list
print list(my3Dvector) #iterate
print my3Dvector[2] #array-like access for elements


<class 'mumodo.InstantIO.SFVec3f'>
-0.365828 -0.748412 0.920436
[-0.365828, -0.748412, 0.920436]
[-0.365828, -0.748412, 0.920436]
0.920436

the rotations have similarly useful functions, e.g.


In [16]:
myrotation = SFRotation(0, 0.5, 0, 0.5)
print myrotation.qx, myrotation.qy, myrotation.qz, myrotation.qw #components of the quaternion
print myrotation.v #get is as a list
print list(myrotation) #iterate
print myrotation[3] #array-like access for elements
print myrotation.attitude, myrotation.bank, myrotation.heading #get as Euler angles in radians


0.0 0.5 0.0 0.5
[0.0, 0.5, 0.0, 0.5]
[0.0, 0.5, 0.0, 0.5]
0.5
0.0 0.0 1.57079632679

Strings are parsed into boolean by the sfbool function, with a different behaviour from the built-in bool: the string True is case-insensitive parsed into True, and any other string is False


In [17]:
sfbool("False"), sfbool("anything"), sfbool("tRue")


Out[17]:
(False, False, True)

In [18]:
bool("False"), bool("anything"), bool("tRue")


Out[18]:
(True, True, True)

Converting into the basic types

What if your own data is not using these types?

Let's say your 3D data looks like below. This is typical if you import your data from CSV files or directly from Excel files, Pandas has functions for that.


In [19]:
mydata = pd.DataFrame([{'2dpositions': [2, 4], 'time': 0},
                       {'2dpositions': [2, 3], 'time': 10},
                       {'2dpositions': [3, 3], 'time': 20},
                       {'2dpositions': [7, 4], 'time': 30},
                       {'2dpositions': [1, 2], 'time': 40},
                       {'2dpositions': [0, 4], 'time': 50}])
mydata


Out[19]:
2dpositions time
0 [2, 4] 0
1 [2, 3] 10
2 [3, 3] 20
3 [7, 4] 30
4 [1, 2] 40
5 [0, 4] 50

First we need to make this look like a streamframe, so we have to make the time into the index (and optionally delete the time column)


In [20]:
mydata.index = mydata.time #alternative syntax to mydata['time']
mydata.index.name = None #we do not need a named index
del mydata['time']
mydata


Out[20]:
2dpositions
0 [2, 4]
10 [2, 3]
20 [3, 3]
30 [7, 4]
40 [1, 2]
50 [0, 4]

Now we want to perhaps change the type of our 2d positions into SFVec2f.


In [21]:
mydata['2dpositions'] = mydata['2dpositions'].map(lambda x: SFVec2f(x[0], x[1]))
mydata


Out[21]:
2dpositions
0 2.0 4.0
10 2.0 3.0
20 3.0 3.0
30 7.0 4.0
40 1.0 2.0
50 0.0 4.0

And thus we have converted our data into the basic mumodo types. This will be very useful for our subsequent analysis

In order to see how to use IntervalFrames, StreamFrames and InstantIO types for analysis, check the other demo notebooks