Skip to content

Collective Measures

API Description
homes_per_location Return the number of users whose home is at each distinct location.
mean_square_displacement Return the mean square displacement (km^2) across all users.
random_location_entropy Return the random entropy for each distinct location across all users.
uncorrelated_location_entropy Return the uncorrelated entropy for each distinct location across all users.
visits_per_location Return the total number of visits for each distinct location.
visits_per_time_unit Return the number of trajectory points per time unit across all users.
od_matrix Compute an Origin-Destination matrix from a trips DataFrame.
od_metrics_per_area Compute per-area mobility metrics from a long-format OD DataFrame.

fastmob.measures.collective.homes_per_location

homes_per_location(traj, *, start_night=22, end_night=7, datetime_col=None, lat_col=None, lng_col=None, uid_col=None)

Return the number of users whose home is at each distinct location.

The number of home locations at location \(j\) is computed as [PRS2016]_:

\[ N_{\text{homes}}(j) = \bigl|\{h_u \mid h_u = j,\, u \in U\}\bigr| \]

where \(h_u\) indicates the home location of individual \(u\) (see :func:~fastmob.measures.individual.home_location) and \(U\) is the set of all individuals.

Parameters:

Name Type Description Default
traj DataFrame - like

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime, latitude, and longitude columns.

required
start_night int

Hour (0–23) at which the nighttime window begins. Default: 22.

22
end_night int

Hour (0–23) at which the nighttime window ends (exclusive). Default: 7.

7
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
lat_col str or None

Explicit latitude column name. Auto-detected when None.

None
lng_col str or None

Explicit longitude column name. Auto-detected when None.

None
uid_col str or None

Explicit user-ID column name. Auto-detected when None.

None

Returns:

Type Description
DataFrame or DataFrame

One row per distinct home location with columns [lat_col, lng_col, "n_homes"], sorted by descending count. The returned backend matches the input backend.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.utils.constants.BRIGHTKITE_SAMPLE
>>> df = pd.read_csv(
...     url,
...     sep="\t",
...     header=0,
...     nrows=5000,
...     names=["uid", "datetime", "lat", "lng", "location id"],
... )
>>> df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
>>> df["location_id"] = df["location id"].astype("string")
>>> df = df.dropna(subset=["uid", "datetime", "lat", "lng"])[
...     ["uid", "datetime", "lat", "lng", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime       lat         lng                              location_id
   0 2010-10-16 06:02:04+00:00 39.891383 -105.070814         7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 39.891077 -105.068532         dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 39.750469 -104.999073 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 39.752713 -104.996337         2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 39.752508 -104.996637         424eb3dd143292f9e013efa00486c907
>>> from fastmob import homes_per_location
>>> result = homes_per_location(df)
>>> print(result.round({"lat": 3, "lng": 3}).head().to_string(index=False))
   lat      lng  n_homes
39.891 -105.069        1
37.630 -122.411        1
39.739 -104.985        1
References
  • [PRS2016] Pappalardo, L., Rinzivillo, S. & Simini, F. (2016) Human Mobility Modelling: exploration and preferential return meet the gravity model. Procedia Computer Science 83, 934-939, http://dx.doi.org/10.1016/j.procs.2016.04.188
See Also

visits_per_location : Total visit count per location across all users. home_location : Inferred home location from nighttime visits (individual measure).


fastmob.measures.collective.mean_square_displacement

mean_square_displacement(traj, *, days=0, hours=1, minutes=0, datetime_col=None, lat_col=None, lng_col=None, uid_col=None)

Return the mean square displacement (km²) across all users.

The mean squared displacement (MSD) measures the average deviation of position from a reference point over time [FS2002] [BHG2006] [SKWB2010]_:

\[ \text{MSD}(t) = \frac{1}{N} \sum_{i=1}^{N} \bigl|r^{(i)}(t) - r^{(i)}(0)\bigr|^2 \]

where \(N\) is the number of individuals, \(r^{(i)}(0)\) is the reference position (first recorded point) of individual \(i\), and \(r^{(i)}(t)\) is their position at time offset \(t\).

Parameters:

Name Type Description Default
traj DataFrame - like

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime, latitude, and longitude columns.

required
days int

Days component of the time offset from each user's start time. Defaults to 0.

0
hours int

Hours component of the time offset. Defaults to 1.

1
minutes int

Minutes component of the time offset. Defaults to 0.

0
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
lat_col str or None

Explicit latitude column name. Auto-detected when None.

None
lng_col str or None

Explicit longitude column name. Auto-detected when None.

None
uid_col str or None

Explicit user-ID column name. Auto-detected when None.

None

Returns:

Type Description
float

Mean square displacement in km². Returns 0.0 when the trajectory is empty or every user's displacement window is trivially at the start.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.utils.constants.BRIGHTKITE_SAMPLE
>>> df = pd.read_csv(
...     url,
...     sep="\t",
...     header=0,
...     nrows=5000,
...     names=["uid", "datetime", "lat", "lng", "location id"],
... )
>>> df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
>>> df["location_id"] = df["location id"].astype("string")
>>> df = df.dropna(subset=["uid", "datetime", "lat", "lng"])[
...     ["uid", "datetime", "lat", "lng", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime       lat         lng                              location_id
   0 2010-10-16 06:02:04+00:00 39.891383 -105.070814         7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 39.891077 -105.068532         dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 39.750469 -104.999073 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 39.752713 -104.996337         2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 39.752508 -104.996637         424eb3dd143292f9e013efa00486c907
>>> from fastmob import mean_square_displacement
>>> result = mean_square_displacement(df)
>>> print(round(result, 3))
128.583
References
  • [FS2002] Frenkel, D. & Smit, B. (2002) Understanding molecular simulation: From algorithms to applications. Academic Press, 196 (2nd Ed.), https://www.sciencedirect.com/book/9780122673511/understanding-molecular-simulation.
  • [BHG2006] Brockmann, D., Hufnagel, L. & Geisel, T. (2006) The scaling laws of human travel. Nature 439, 462-465, https://www.nature.com/articles/nature04292
  • [SKWB2010] Song, C., Koren, T., Wang, P. & Barabasi, A.L. (2010) Modelling the scaling properties of human mobility. Nature Physics 6, 818-823, https://www.nature.com/articles/nphys1760

fastmob.measures.collective.random_location_entropy

random_location_entropy(traj, *, datetime_col=None, lat_col=None, lng_col=None, uid_col=None)

Return the random entropy for each distinct location across all users.

Random location entropy is \(\log_2(n)\), where \(n\) is the number of distinct users who visited the location. A location is a unique exact (lat, lng) pair — matching the skmob convention.

Parameters:

Name Type Description Default
traj DataFrame - like

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime, latitude, and longitude columns. A user-ID column is optional; when absent all rows are treated as coming from a single individual (entropy will be 0 everywhere).

required
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
lat_col str or None

Explicit latitude column name. Auto-detected when None.

None
lng_col str or None

Explicit longitude column name. Auto-detected when None.

None
uid_col str or None

Explicit user-ID column name. Auto-detected when None.

None

Returns:

Type Description
DataFrame or DataFrame

One row per distinct (lat, lng) location with columns [lat_col, lng_col, "random_entropy"]. The returned backend matches the input backend.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.utils.constants.BRIGHTKITE_SAMPLE
>>> df = pd.read_csv(
...     url,
...     sep="\t",
...     header=0,
...     nrows=5000,
...     names=["uid", "datetime", "lat", "lng", "location id"],
... )
>>> df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
>>> df["location_id"] = df["location id"].astype("string")
>>> df = df.dropna(subset=["uid", "datetime", "lat", "lng"])[
...     ["uid", "datetime", "lat", "lng", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime       lat         lng                              location_id
   0 2010-10-16 06:02:04+00:00 39.891383 -105.070814         7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 39.891077 -105.068532         dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 39.750469 -104.999073 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 39.752713 -104.996337         2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 39.752508 -104.996637         424eb3dd143292f9e013efa00486c907
>>> from fastmob import random_location_entropy
>>> result = random_location_entropy(df)
>>> print(result.round({"random_entropy": 3}).head().to_string(index=False))
      lat        lng  random_entropy
 0.000000   0.000000           1.585
29.532220 -98.300914           0.000
29.942673 -90.064455           0.000
29.948116 -90.063436           0.000
29.948125 -90.063510           0.000
See Also

uncorrelated_location_entropy : Location entropy weighted by visitor frequency.


fastmob.measures.collective.uncorrelated_location_entropy

uncorrelated_location_entropy(traj, *, datetime_col=None, lat_col=None, lng_col=None, uid_col=None)

Return the uncorrelated entropy for each distinct location across all users.

For each location l, computes the Shannon entropy over the distribution of visit probabilities:

\[ S_\mathrm{unc}(l) = -\sum_u p(u, l) \log_2(p(u, l)) \]

where \(p(u, l)\) is the fraction of all visits to \(l\) that belong to user \(u\). A location is a unique exact (lat, lng) pair.

Parameters:

Name Type Description Default
traj DataFrame - like

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime, latitude, and longitude columns. A user-ID column is optional; when absent all rows are treated as coming from a single individual (entropy will be 0 everywhere).

required
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
lat_col str or None

Explicit latitude column name. Auto-detected when None.

None
lng_col str or None

Explicit longitude column name. Auto-detected when None.

None
uid_col str or None

Explicit user-ID column name. Auto-detected when None.

None

Returns:

Type Description
DataFrame or DataFrame

One row per distinct (lat, lng) location with columns [lat_col, lng_col, "uncorrelated_entropy"]. The returned backend matches the input backend.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.utils.constants.BRIGHTKITE_SAMPLE
>>> df = pd.read_csv(
...     url,
...     sep="\t",
...     header=0,
...     nrows=5000,
...     names=["uid", "datetime", "lat", "lng", "location id"],
... )
>>> df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
>>> df["location_id"] = df["location id"].astype("string")
>>> df = df.dropna(subset=["uid", "datetime", "lat", "lng"])[
...     ["uid", "datetime", "lat", "lng", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime       lat         lng                              location_id
   0 2010-10-16 06:02:04+00:00 39.891383 -105.070814         7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 39.891077 -105.068532         dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 39.750469 -104.999073 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 39.752713 -104.996337         2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 39.752508 -104.996637         424eb3dd143292f9e013efa00486c907
>>> from fastmob import uncorrelated_location_entropy
>>> result = uncorrelated_location_entropy(df)
>>> print(result.round({"uncorrelated_entropy": 3}).head().to_string(index=False))
      lat        lng  uncorrelated_entropy
 0.000000   0.000000                 0.911
29.532220 -98.300914                 0.000
29.942673 -90.064455                 0.000
29.948116 -90.063436                 0.000
29.948125 -90.063510                 0.000
References
  • [CML2011] Cho, E., Myers, S. A. & Leskovec, J. (2011) Friendship and mobility: user movement in location-based social networks. In Proceedings of the 17th ACM SIGKDD international conference on Knowledge discovery and data mining, 1082-1090, https://dl.acm.org/citation.cfm?id=2020579
See Also

random_location_entropy : Baseline entropy assuming uniform visitation across users.


fastmob.measures.collective.visits_per_location

visits_per_location(traj, *, datetime_col=None, lat_col=None, lng_col=None, uid_col=None)

Return the total number of visits for each distinct location.

Counts all trajectory rows (visits) per unique (lat, lng) pair across all users. This is the population-level analogue of per-user location_frequency.

Parameters:

Name Type Description Default
traj DataFrame - like

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime, latitude, and longitude columns.

required
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
lat_col str or None

Explicit latitude column name. Auto-detected when None.

None
lng_col str or None

Explicit longitude column name. Auto-detected when None.

None
uid_col str or None

Explicit user-ID column name. Auto-detected when None (not used for aggregation, but retained for consistent preprocessing).

None

Returns:

Type Description
DataFrame or DataFrame

One row per distinct (lat, lng) location with columns [lat_col, lng_col, "n_visits"], sorted by descending visit count. The returned backend matches the input backend.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.utils.constants.BRIGHTKITE_SAMPLE
>>> df = pd.read_csv(
...     url,
...     sep="\t",
...     header=0,
...     nrows=5000,
...     names=["uid", "datetime", "lat", "lng", "location id"],
... )
>>> df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
>>> df["location_id"] = df["location id"].astype("string")
>>> df = df.dropna(subset=["uid", "datetime", "lat", "lng"])[
...     ["uid", "datetime", "lat", "lng", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime       lat         lng                              location_id
   0 2010-10-16 06:02:04+00:00 39.891383 -105.070814         7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 39.891077 -105.068532         dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 39.750469 -104.999073 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 39.752713 -104.996337         2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 39.752508 -104.996637         424eb3dd143292f9e013efa00486c907
>>> from fastmob import visits_per_location
>>> result = visits_per_location(df)
>>> print(result.head().to_string(index=False))
      lat         lng  n_visits
39.739154 -104.984703       340
37.630490 -122.411084       297
37.580304 -122.343679       297
37.584103 -122.366083       249
39.762146 -104.982480       232
References
  • [PF2018] Pappalardo, L. & Simini, F. (2018) Data-driven generation of spatio-temporal routines in human mobility. Data Mining and Knowledge Discovery 32, 787-829, https://link.springer.com/article/10.1007/s10618-017-0548-4
See Also

homes_per_location : Number of users whose home is at each location. location_frequency : Per-user visit frequency (individual measure).


fastmob.measures.collective.visits_per_time_unit

visits_per_time_unit(traj, time_unit=None, *, freq='1h', datetime_col=None, lat_col=None, lng_col=None, uid_col=None)

Return the number of trajectory points per time unit across all users.

Groups all trajectory records by time bins of width freq and counts the number of records (visits) in each bin. The result covers only bins that contain at least one record.

The freq parameter follows pandas offset alias syntax (e.g. "1h", "1D", "15min"). Results are returned using the same dataframe backend as the input.

Parameters:

Name Type Description Default
traj DataFrame - like

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime, latitude, and longitude columns.

required
time_unit str or None

Alias for freq (skmob compatibility). When provided, overrides freq.

None
freq str

Pandas-compatible offset alias for the time bin width. Default: "1h".

'1h'
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
lat_col str or None

Explicit latitude column name. Auto-detected when None.

None
lng_col str or None

Explicit longitude column name. Auto-detected when None.

None
uid_col str or None

Explicit user-ID column name. Auto-detected when None.

None

Returns:

Type Description
DataFrame or DataFrame

One row per non-empty time bin with columns [datetime_col, "n_visits"], sorted chronologically.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.utils.constants.BRIGHTKITE_SAMPLE
>>> df = pd.read_csv(
...     url,
...     sep="\t",
...     header=0,
...     nrows=5000,
...     names=["uid", "datetime", "lat", "lng", "location id"],
... )
>>> df["datetime"] = pd.to_datetime(df["datetime"], errors="coerce")
>>> df["location_id"] = df["location id"].astype("string")
>>> df = df.dropna(subset=["uid", "datetime", "lat", "lng"])[
...     ["uid", "datetime", "lat", "lng", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime       lat         lng                              location_id
   0 2010-10-16 06:02:04+00:00 39.891383 -105.070814         7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 39.891077 -105.068532         dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 39.750469 -104.999073 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 39.752713 -104.996337         2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 39.752508 -104.996637         424eb3dd143292f9e013efa00486c907
>>> from fastmob import visits_per_time_unit
>>> result = visits_per_time_unit(df)
>>> print(result.head().to_string(index=False))
                 datetime  n_visits
2008-06-21 17:00:00+00:00         1
2008-06-22 01:00:00+00:00         1
2008-06-22 05:00:00+00:00         1
2008-06-22 17:00:00+00:00         1
2008-06-22 18:00:00+00:00         1
References
  • [PRS2016] Pappalardo, L., Rinzivillo, S. & Simini, F. (2016) Human Mobility Modelling: exploration and preferential return meet the gravity model. Procedia Computer Science 83, 934-939, http://dx.doi.org/10.1016/j.procs.2016.04.188

fastmob.measures.collective.od_matrix(trips, origin_col=None, destination_col=None)

Compute an Origin-Destination matrix from a trips DataFrame.

Groups trips by (origin, destination) pairs and counts them. Returns a long-format DataFrame with one row per observed origin-destination pair, in the same backend as the input.

Parameters:

Name Type Description Default
trips DataFrame - like

A DataFrame (pandas, polars, or any Narwhals-compatible backend) with at least two columns representing origin and destination areas.

required
origin_col str or None

Column name for the origin area. Auto-detected if None.

None
destination_col str or None

Column name for the destination area. Auto-detected if None.

None

Returns:

Type Description
DataFrame or DataFrame

Long-format OD counts with columns [origin_col, destination_col, "count"], returned in the caller's original backend.

Examples:

>>> import pandas as pd
>>> from fastmob.measures.collective import od_matrix
>>> trips = pd.DataFrame(
...     {
...         "origin_area": ["A", "A", "A", "B", "B", "C"],
...         "destination_area": ["A", "B", "B", "A", "C", "A"],
...     }
... )
>>> result = od_matrix(trips)
>>> print(result.to_string(index=False))
origin_area destination_area  count
          A                A      1
          A                B      2
          B                A      1
          B                C      1
          C                A      1

fastmob.measures.collective.od_metrics_per_area(od_df, origin_col=None, destination_col=None)

Compute per-area mobility metrics from a long-format OD DataFrame.

Takes the long-format OD DataFrame returned by :func:od_matrix and computes MoveInside, InComing, OutGoing, and Total flows for each area.

Parameters:

Name Type Description Default
od_df DataFrame - like

Long-format OD DataFrame as returned by :func:od_matrix, with columns [origin_col, destination_col, "count"].

required
origin_col str or None

Column name for the origin area. Auto-detected if None.

None
destination_col str or None

Column name for the destination area. Auto-detected if None.

None

Returns:

Type Description
DataFrame or DataFrame

DataFrame with columns ["area_code", "MoveInside", "InComing", "OutGoing", "Total"], one row per area, returned in the caller's original backend.

Examples:

>>> import pandas as pd
>>> from fastmob.measures.collective import od_matrix, od_metrics_per_area
>>> trips = pd.DataFrame(
...     {
...         "origin_area": ["A", "A", "A", "B", "B", "C"],
...         "destination_area": ["A", "B", "B", "A", "C", "A"],
...     }
... )
>>> od = od_matrix(trips)
>>> result = od_metrics_per_area(od)
>>> print(result.to_string(index=False))
area_code  MoveInside  InComing  OutGoing  Total
        A           1         2         2      5
        B           0         2         2      4
        C           0         1         1      2