Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Tutorial 1 (Introductory): Visualizing Timeseries

Authors
Affiliations
University of Oxford
University of Toronto / University of Cambridge

Now that you’ve learned the fundamentals of neuroimaging and the connectomics workflow—including concepts such as fMRI timeseries, brain parcellation, and functional connectivity (FC)—it’s time to put those ideas into practice using LEMON dataset. In this notebook, we’ll learn how to load, explore, subset, and visualize brain timeseries data using common Python libraries.

Working with Timeseries Data

In this notebook, we will:

  1. Load a CSV file from the ts/ folder inside our project directory

  2. Inspect the data

  3. Get the Schaefer atlas region names with nilearn

  4. Subset one subject

  5. Subset one brain region

  6. Plot that region’s activity over time

Below, we’ll be importing some python packages that are required for this script. Common packages are

  • numpy and pandas to deal with numbers and dataframes (i.e., tables)

  • matplotlib for data visualization

  • pathlib for managing your file paths/directories

  • nilearn for anything to do with neuroimaging data

Feel free to run the code below to “import” these packages, which is a way to let the code knows that we’ll be using these specific packages.

1. Set Up the File Path

First, we will define the directory where this notebook is using the function or command “cwd” from the “Path” package that we have imported from above from pathlib import Path

cwd = current working directory

We’ll be calling this current working directory as your project directory.

Then, where your code is, create a folder called “ts”. Within this folder “ts”, we’ll store our timeseries file in Schaefer parcellation (schaefer100_timeseries_long.csv).

With this, try to understand what the code below is doing!

Project directory: /Users/shemrock/Library/CloudStorage/OneDrive-Nexus365/Clematis/guide/Conn_comp/competition2026/lemon2026
Timeseries file: /Users/shemrock/Library/CloudStorage/OneDrive-Nexus365/Clematis/guide/Conn_comp/competition2026/lemon2026/ts/schaefer100_timeseries_long.csv

2. Load the CSV File

Next, we need to convert (or import) the csv file into a “dataframe” format.

A DataFrame is like a spreadsheet: rows and columns are organized in a table. Dataframes make it easy for us to manipulate large datasets with many rows and columns.

We use pandas.read_csv() to import the data into a DataFrame (i.e., read_csv function from the pandas package)

If you have imported the packages and indicated your file paths correctly, you should be able to run the code below.

Shape of dataframe: (143440, 101)
Loading...

3. Understand the Structure of the Dataframe

Now that we have imported the timeseries data from the csv into the dataframe format (stored in the timeseries_df variable), let’s try to make sense of our dataframe or the timeseries data!

Dimension

First, let’s check the shape or the dimension of the dataframe using .shape from pandas.

Shape of dataframe: (143440, 101)

This returns: (143440, 101). Recall that a dataframe’s shape is reported as: (number_of_rows, number_of_columns). Therefore, our dataset contains 143,440 rows and 101 columns

The 101 columns consist of:

  • 1 column containing the subject ID (subject)

  • 100 columns containing brain activity values from the Schaefer100 atlas regions

The 143,440 rows contain data from all subjects combined.

Since this dataset contains 220 subjects, each with 652 fMRI time points, we have: 220 subjects × 652 time points = 143,440 rows

In other words, this CSV file stores the complete timeseries dataset for every participant in a single table.

Previewing Your Dataframe

Timeseries data from neuroimaging is often very large. However, we can use head from pandas to see the first few rows and some columns from our dataframe.

Loading...

Using head(), we can see from the above that the first column contains the subject ID, while the remaining columns contain activity values from Schaefer100 brain regions (for example, 7Networks_LH_Vis_1, 7Networks_LH_Vis_2, and so on).

Each row represents a single fMRI time point from one subject:

  • each row = one fMRI time point

  • first column = subject identifier

  • remaining columns = brain activity values for each Schaefer100 brain region

  • consecutive rows for the same subject form that subject’s fMRI timeseries (we’ll be visualizing this timeseries later!)

Remember, all subjects are stored together in the same dataframe.

To gain some more intuition to this dataset, let’s do two things:

  • get a list of column names and subject names

  • extract the data just for a single subject and explore their timeseries before moving on to analyses involving the full dataset.

List of Brain Regions (Columns) and Subject Ids (Rows)

Columns (brain regions)

Recall that each column corresponds to either:

  • the subject identifier (subject)

  • a Schaefer100 brain region

The code below converts the dataframe’s column names into a Python list and prints them.

A list is one of Python’s basic data structures used to store multiple items in a specific order. You can think of a list like a numbered collection, where each item has a position and can be accessed individually.

For example, a list can store many brain region names, allowing us to access, inspect, or loop through them one at a time.

For the code below, all you need to know is that we are applying .tolist() to the dataframe to create a new list containing all their column names.

['subject', '7Networks_LH_Vis_1', '7Networks_LH_Vis_2', '7Networks_LH_Vis_3', '7Networks_LH_Vis_4', '7Networks_LH_Vis_5', '7Networks_LH_Vis_6', '7Networks_LH_Vis_7', '7Networks_LH_Vis_8', '7Networks_LH_Vis_9', '7Networks_LH_SomMot_1', '7Networks_LH_SomMot_2', '7Networks_LH_SomMot_3', '7Networks_LH_SomMot_4', '7Networks_LH_SomMot_5', '7Networks_LH_SomMot_6', '7Networks_LH_DorsAttn_Post_1', '7Networks_LH_DorsAttn_Post_2', '7Networks_LH_DorsAttn_Post_3', '7Networks_LH_DorsAttn_Post_4', '7Networks_LH_DorsAttn_Post_5', '7Networks_LH_DorsAttn_Post_6', '7Networks_LH_DorsAttn_PrCv_1', '7Networks_LH_DorsAttn_FEF_1', '7Networks_LH_SalVentAttn_ParOper_1', '7Networks_LH_SalVentAttn_FrOperIns_1', '7Networks_LH_SalVentAttn_FrOperIns_2', '7Networks_LH_SalVentAttn_PFCl_1', '7Networks_LH_SalVentAttn_Med_1', '7Networks_LH_SalVentAttn_Med_2', '7Networks_LH_SalVentAttn_Med_3', '7Networks_LH_Limbic_OFC_1', '7Networks_LH_Limbic_TempPole_1', '7Networks_LH_Limbic_TempPole_2', '7Networks_LH_Cont_Par_1', '7Networks_LH_Cont_PFCl_1', '7Networks_LH_Cont_pCun_1', '7Networks_LH_Cont_Cing_1', '7Networks_LH_Default_Temp_1', '7Networks_LH_Default_Temp_2', '7Networks_LH_Default_Par_1', '7Networks_LH_Default_Par_2', '7Networks_LH_Default_PFC_1', '7Networks_LH_Default_PFC_2', '7Networks_LH_Default_PFC_3', '7Networks_LH_Default_PFC_4', '7Networks_LH_Default_PFC_5', '7Networks_LH_Default_PFC_6', '7Networks_LH_Default_PFC_7', '7Networks_LH_Default_pCunPCC_1', '7Networks_LH_Default_pCunPCC_2', '7Networks_RH_Vis_1', '7Networks_RH_Vis_2', '7Networks_RH_Vis_3', '7Networks_RH_Vis_4', '7Networks_RH_Vis_5', '7Networks_RH_Vis_6', '7Networks_RH_Vis_7', '7Networks_RH_Vis_8', '7Networks_RH_SomMot_1', '7Networks_RH_SomMot_2', '7Networks_RH_SomMot_3', '7Networks_RH_SomMot_4', '7Networks_RH_SomMot_5', '7Networks_RH_SomMot_6', '7Networks_RH_SomMot_7', '7Networks_RH_SomMot_8', '7Networks_RH_DorsAttn_Post_1', '7Networks_RH_DorsAttn_Post_2', '7Networks_RH_DorsAttn_Post_3', '7Networks_RH_DorsAttn_Post_4', '7Networks_RH_DorsAttn_Post_5', '7Networks_RH_DorsAttn_PrCv_1', '7Networks_RH_DorsAttn_FEF_1', '7Networks_RH_SalVentAttn_TempOccPar_1', '7Networks_RH_SalVentAttn_TempOccPar_2', '7Networks_RH_SalVentAttn_FrOperIns_1', '7Networks_RH_SalVentAttn_Med_1', '7Networks_RH_SalVentAttn_Med_2', '7Networks_RH_Limbic_OFC_1', '7Networks_RH_Limbic_TempPole_1', '7Networks_RH_Cont_Par_1', '7Networks_RH_Cont_Par_2', '7Networks_RH_Cont_PFCl_1', '7Networks_RH_Cont_PFCl_2', '7Networks_RH_Cont_PFCl_3', '7Networks_RH_Cont_PFCl_4', '7Networks_RH_Cont_Cing_1', '7Networks_RH_Cont_PFCmp_1', '7Networks_RH_Cont_pCun_1', '7Networks_RH_Default_Par_1', '7Networks_RH_Default_Temp_1', '7Networks_RH_Default_Temp_2', '7Networks_RH_Default_Temp_3', '7Networks_RH_Default_PFCv_1', '7Networks_RH_Default_PFCv_2', '7Networks_RH_Default_PFCdPFCm_1', '7Networks_RH_Default_PFCdPFCm_2', '7Networks_RH_Default_PFCdPFCm_3', '7Networks_RH_Default_pCunPCC_1', '7Networks_RH_Default_pCunPCC_2']

Obtaining list of unique subject IDs:

Let’s try to obtain a list of unique subject IDs.

The results from the print(column_names) show that the first column is the ‘subject’ column. (Remember, list stores values in a specific order).

The dataframe contains many rows for each subject because every subject has multiple fMRI time points.

For example, subject sub-010002 may appear 652 times because that participant has 652 time points.

If we simply print the subject column, we would see many repeated subject IDs. The output shown after printing the subject column shows that there are 143440 rows (Length: 143440).

0         sub-010002
1         sub-010002
2         sub-010002
3         sub-010002
4         sub-010002
             ...    
143435    sub-010321
143436    sub-010321
143437    sub-010321
143438    sub-010321
143439    sub-010321
Name: subject, Length: 143440, dtype: str

So the next step is to find unique or distinct subject IDs that appear in subject_column.

In pandas, we can use .unique() to find distinct values in a single column, which is what we need to find distinct subject IDs that appear in subject_column.

['sub-010002', 'sub-010003', 'sub-010004', 'sub-010005', 'sub-010006', 'sub-010007', 'sub-010010', 'sub-010012', 'sub-010015', 'sub-010016', 'sub-010017', 'sub-010019', 'sub-010020', 'sub-010021', 'sub-010022', 'sub-010023', 'sub-010024', 'sub-010026', 'sub-010027', 'sub-010028', 'sub-010029', 'sub-010030', 'sub-010031', 'sub-010032', 'sub-010033', 'sub-010034', 'sub-010035', 'sub-010036', 'sub-010037', 'sub-010038', 'sub-010039', 'sub-010040', 'sub-010041', 'sub-010042', 'sub-010043', 'sub-010044', 'sub-010045', 'sub-010046', 'sub-010048', 'sub-010050', 'sub-010051', 'sub-010052', 'sub-010053', 'sub-010056', 'sub-010059', 'sub-010060', 'sub-010061', 'sub-010062', 'sub-010063', 'sub-010064', 'sub-010065', 'sub-010066', 'sub-010067', 'sub-010068', 'sub-010069', 'sub-010070', 'sub-010071', 'sub-010072', 'sub-010073', 'sub-010074', 'sub-010075', 'sub-010076', 'sub-010077', 'sub-010078', 'sub-010079', 'sub-010080', 'sub-010081', 'sub-010083', 'sub-010084', 'sub-010085', 'sub-010086', 'sub-010087', 'sub-010088', 'sub-010089', 'sub-010090', 'sub-010091', 'sub-010092', 'sub-010093', 'sub-010094', 'sub-010100', 'sub-010104', 'sub-010110', 'sub-010126', 'sub-010134', 'sub-010136', 'sub-010137', 'sub-010138', 'sub-010141', 'sub-010142', 'sub-010146', 'sub-010148', 'sub-010150', 'sub-010152', 'sub-010155', 'sub-010157', 'sub-010162', 'sub-010163', 'sub-010164', 'sub-010165', 'sub-010166', 'sub-010168', 'sub-010169', 'sub-010170', 'sub-010176', 'sub-010183', 'sub-010191', 'sub-010192', 'sub-010193', 'sub-010194', 'sub-010195', 'sub-010196', 'sub-010197', 'sub-010199', 'sub-010200', 'sub-010201', 'sub-010202', 'sub-010203', 'sub-010204', 'sub-010207', 'sub-010210', 'sub-010213', 'sub-010214', 'sub-010215', 'sub-010216', 'sub-010218', 'sub-010219', 'sub-010220', 'sub-010222', 'sub-010223', 'sub-010224', 'sub-010225', 'sub-010226', 'sub-010228', 'sub-010229', 'sub-010231', 'sub-010232', 'sub-010233', 'sub-010234', 'sub-010235', 'sub-010236', 'sub-010237', 'sub-010238', 'sub-010239', 'sub-010240', 'sub-010241', 'sub-010242', 'sub-010243', 'sub-010244', 'sub-010245', 'sub-010246', 'sub-010247', 'sub-010250', 'sub-010252', 'sub-010253', 'sub-010254', 'sub-010255', 'sub-010256', 'sub-010257', 'sub-010260', 'sub-010261', 'sub-010262', 'sub-010263', 'sub-010264', 'sub-010265', 'sub-010266', 'sub-010267', 'sub-010268', 'sub-010269', 'sub-010270', 'sub-010271', 'sub-010272', 'sub-010273', 'sub-010274', 'sub-010275', 'sub-010276', 'sub-010277', 'sub-010278', 'sub-010279', 'sub-010280', 'sub-010281', 'sub-010282', 'sub-010283', 'sub-010284', 'sub-010285', 'sub-010286', 'sub-010287', 'sub-010288', 'sub-010289', 'sub-010290', 'sub-010291', 'sub-010292', 'sub-010293', 'sub-010294', 'sub-010295', 'sub-010296', 'sub-010297', 'sub-010298', 'sub-010299', 'sub-010300', 'sub-010301', 'sub-010302', 'sub-010303', 'sub-010304', 'sub-010305', 'sub-010306', 'sub-010307', 'sub-010308', 'sub-010309', 'sub-010310', 'sub-010311', 'sub-010312', 'sub-010313', 'sub-010314', 'sub-010315', 'sub-010316', 'sub-010317', 'sub-010318', 'sub-010319', 'sub-010320', 'sub-010321']
220

4. Subset the Dataframe for One Subject

Right now, our dataframe contains the timeseries data for all 220 subjects. While this is ideal for large-scale analyses, it is often easier to understand the structure of the data by first examining a single participant.

Let’s extract all time points belonging to a single participant: sub-010003.

Because each row corresponds to one time point, the resulting dataframe will contain many rows (for example, ~652 rows) rather than a single row.

Shape: (652, 101)
Loading...
Loading...

5. Extract a Single Brain Region Timeseries of a Subject

Now let’s focus on one brain region:

7Networks_LH_Vis_1

This region belongs to the left hemisphere visual network.

By selecting this column from our subject dataframe, we obtain a single timeseries showing how activity in this region changes over time.

(652,)
0 0.304409 1 0.291162 2 0.287490 3 0.242856 4 0.217101 Name: 7Networks_LH_Vis_1, dtype: float64

6. Visualize the Timeseries

We can now visualize how activity changes over time in this brain region.

The x-axis represents fMRI time points.

The y-axis represents the activity value recorded for this brain region at each time point.

We’ll be using matplotlib to plot our data.

<Figure size 1200x400 with 1 Axes>

Understanding the plot

The plot() function draws a line connecting the activity values recorded at each time point.

In this figure:

  • the x-axis represents time points in the fMRI scan

  • the y-axis represents activity in the selected brain region

  • each point on the line corresponds to one row in the subject’s timeseries dataframe

Notice how the signal rises and falls over time. These fluctuations are the fMRI signal measured from this brain region. Later, we will compare these fluctuations across different brain regions to estimate functional connectivity.