Skip to content

Collective measures

Collective measures describe where and when a whole population moves: homes and visits per place, visits over time, origin-destination flows, entropy of places and spatio-temporal occupancy. For a guided introduction see Measuring people and populations; worked examples are in Population measures and flows.

Where and when people are

API Description Example
homes_per_location Return the number of users whose home is at each distinct location. Homes and visits per place
visits_per_location Return the total number of visits for each distinct location. Homes and visits per place
visits_per_time_unit Return the number of trajectory points per time unit across all users. Homes and visits per place

How far people spread

API Description Example
mean_square_displacement Return the mean square displacement (km²) across all users. How far do people spread over time?

How many different people use a place

API Description Example
random_location_entropy Return the random entropy for each distinct location across all users. How many different people use a place?
uncorrelated_location_entropy Return the uncorrelated entropy for each distinct location across all users. How many different people use a place?

Flows between areas

API Description Example
od_matrix Compute an Origin-Destination matrix from a trips DataFrame. From check-ins to flows
od_metrics_per_area Compute per-area mobility metrics from a long-format OD DataFrame. From check-ins to flows
mean_area_volume Mean user volume per area and 10-minute time bin, averaged across days of the week. When and where is each place busy?

Comparing and linking populations

API Description Example
build_stvd Aggregate staypoints against a global Locations catalogue into an STVD frame. When and where is each place busy?
interest_network Return pairs of global locations visited by the same people. When and where is each place busy?

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 home_location) and \(U\) is the set of all individuals.

Parameters:

Name Type Description Default
traj dataframe

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
pandas or Polars 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.data.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).

Worked example: Homes and visits per place


visits_per_location(traj, *, datetime_col=None, location_id_col=None, uid_col=None)

Return the total number of visits for each distinct location.

Counts all trajectory rows (visits) per unique location ID across all users. Location IDs must be global-scoped, so the same ID represents the same location for every user. This is the population-level analogue of per-user location_frequency.

Parameters:

Name Type Description Default
traj dataframe

Trajectory dataframe; any Narwhals-compatible eager backend (pandas, polars, …). Must have datetime and a globally scoped location-ID column.

required
datetime_col str or None

Explicit datetime column name. Auto-detected when None.

None
location_id_col str or None

Explicit location-ID column name. Auto-detected when None. IDs must be global-scoped rather than user-scoped.

None
uid_col str or None

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

None

Returns:

Type Description
pandas or Polars DataFrame

One row per distinct location ID with columns [location_id_col, "n_visits"], sorted by descending visit count. The returned backend matches the input backend.

Examples:

>>> import pandas as pd
>>> import fastmob
>>> url = fastmob.data.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", "location_id"])[
...     ["uid", "datetime", "location_id"]
... ]
>>> print(df.head().to_string(index=False))
 uid                  datetime                       location_id
   0 2010-10-16 06:02:04+00:00 7a0f88982aa015062b95e3b4843f9ca2
   0 2010-10-16 03:48:54+00:00 dd7cd3d264c2d063832db506fba8bf79
   0 2010-10-14 18:25:51+00:00 9848afcc62e500a01cf6fbf24b797732f8963683
   0 2010-10-14 00:21:47+00:00 2ef143e12038c870038df53e0478cefc
   0 2010-10-13 23:31:51+00:00 424eb3dd143292f9e013efa00486c907
>>> from fastmob import visits_per_location
>>> result = visits_per_location(df)
>>> print(result.head().to_string(index=False))
                          location_id  n_visits
7a0f88982aa015062b95e3b4843f9ca2              340
dd7cd3d264c2d063832db506fba8bf79              297
9848afcc62e500a01cf6fbf24b797732f8963683       297
2ef143e12038c870038df53e0478cefc               249
424eb3dd143292f9e013efa00486c907               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).

Worked example: Homes and visits per place


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

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, kept for compatibility with scikit-mobility. 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
pandas or Polars 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.data.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

Worked example: Homes and visits per place


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

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.data.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

Worked example: How far do people spread over time?


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, as in scikit-mobility.

Parameters:

Name Type Description Default
traj dataframe

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
pandas or Polars 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.data.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.

Worked example: How many different people use a place?


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

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
pandas or Polars 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.data.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.

Worked example: How many different people use a place?


od_matrix(trips, origin_col=None, destination_col=None, sort=True, weight_col=None)

Compute an Origin-Destination matrix from a trips DataFrame.

Groups trips by (origin, destination) pairs and counts them, or sums their weights when weight_col is provided. 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

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
sort bool

When True (default), sort the result by [origin_col, destination_col]. Order-insensitive callers (e.g. building a FlowDataFrame for CPC) can pass False to skip that sort.

True
weight_col str or None

Numeric column to sum for each pair. If None, count trips instead. Null and NaN weights contribute zero; pairs with only missing weights are retained with a sum of zero. The column must have a numeric dtype.

None

Returns:

Type Description
pandas or Polars DataFrame

Long-format OD counts or weight sums 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

Worked example: From check-ins to flows


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 od_matrix and computes MoveInside, InComing, OutGoing, and Total flows for each area.

Parameters:

Name Type Description Default
od_df dataframe

Long-format OD DataFrame as returned by 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
pandas or Polars 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

Worked example: From check-ins to flows


mean_area_volume(visits, *, area_col=None, user_id_col=None, start_col=None, end_col=None)

Mean user volume per area and 10-minute time bin, averaged across days of the week.

For each area the function:

  1. Expands each stay into 10-minute presence slots.
  2. Counts unique users per slot per calendar date.
  3. Averages counts over dates for each day-of-week.
  4. Averages the 7 day-of-week means (missing days contribute 0).

Parameters:

Name Type Description Default
visits dataframe

Any Narwhals-compatible eager dataframe with one row per stay event.

required
area_col str or None

Area column name. Auto-detected from LOCATION_CANDIDATES when None.

None
user_id_col str or None

User ID column. Auto-detected from USER_ID_CANDIDATES when None.

None
start_col str or None

Stay start timestamp. Auto-detected from TIMESTAMP_CANDIDATES when None.

None
end_col str or None

Stay end timestamp. Auto-detected from ["end_timestamp", "end_time"] when None.

None

Returns:

Type Description
pandas or Polars DataFrame

Same backend as input. Columns: area, time_bin ("HH:MM"), mean_volume. Rows where mean_volume == 0 are excluded. Sorted by (area, time_bin).

Raises:

Type Description
ValueError

If any required column cannot be detected.

Notes
  • A stay where start == end produces exactly one bin (the floored start).
  • A stay where start > end produces no bins and is silently skipped.
  • Stays spanning midnight are handled correctly: bins are attributed to the date on which each bin falls.

Examples:

>>> import pandas as pd
>>> from fastmob.measures.collective import mean_area_volume
>>> visits = pd.DataFrame(
...     {
...         "user_id": ["u1", "u2", "u1"],
...         "area": ["home", "home", "work"],
...         "start_timestamp": pd.to_datetime(
...             ["2020-01-01 08:00", "2020-01-01 08:10", "2020-01-01 09:00"]
...         ),
...         "end_timestamp": pd.to_datetime(
...             ["2020-01-01 08:20", "2020-01-01 08:20", "2020-01-01 09:10"]
...         ),
...     }
... )
>>> result = mean_area_volume(visits)
>>> print(result.round({"mean_volume": 3}).to_string(index=False))
area time_bin  mean_volume
home    08:00        0.143
home    08:10        0.286
home    08:20        0.286
work    09:00        0.143
work    09:10        0.143

Worked example: When and where is each place busy?


build_stvd(staypoints, locations, *, location_id_col='location_id')

Aggregate staypoints against a global Locations catalogue into an STVD frame.

Combines mean_area_volume (per-location, per-time-bin mean visitor volume) with each location's centroid from locations, producing the (location_id, time_bin, mean_volume, center_lat, center_lng) shape fastmob.measures.evaluation.stvd_emd consumes directly. Call this once per side against the same locations catalogue so the two resulting distributions are defined over the same physical places, then compare them with stvd_emd.

Parameters:

Name Type Description Default
staypoints Staypoints or dataframe

Staypoints already assigned to locations (e.g. via Staypoints.generate_global_locations or Staypoints.associate_global_locations). Raw dataframes are wrapped in a Staypoints instance with auto-detected columns.

required
locations Locations

A global-scope Locations catalogue (see Locations.require_global).

required
location_id_col str

Column on staypoints holding each staypoint's assigned location ID. Default "location_id".

'location_id'

Returns:

Type Description
dataframe

Same backend as staypoints. Columns: location_id, time_bin, mean_volume, center_lat, center_lng.

Raises:

Type Description
ValueError

If locations is not global-scoped, or staypoints references location IDs absent from locations.

Examples:

>>> import pandas as pd
>>> from fastmob.core import Locations, Staypoints
>>> from fastmob.measures.collective import build_stvd
>>> staypoints = Staypoints(
...     pd.DataFrame(
...         {
...             "uid": ["u1", "u2"],
...             "lat": [0.0, 0.0],
...             "lng": [0.0, 0.0],
...             "started_at": pd.to_datetime(["2020-01-01 08:00", "2020-01-01 08:00"]),
...             "finished_at": pd.to_datetime(["2020-01-01 08:00", "2020-01-01 08:00"]),
...             "location_id": [1, 1],
...         }
...     )
... )
>>> locations = Locations(
...     pd.DataFrame({"location_id": [1], "center_lat": [0.0], "center_lng": [0.0]}),
...     scope="global",
... )
>>> build_stvd(staypoints, locations)
   location_id time_bin  mean_volume  center_lat  center_lng
0            1    08:00     0.285714         0.0         0.0

Worked example: When and where is each place busy?


interest_network(staypoints, locations)

Return pairs of global locations visited by the same people.

Each output row is an undirected edge. n_people is the number of distinct users that visited both endpoint locations at least once; repeated staypoints do not increase an edge's weight. Endpoint orientation and output rows follow the supplied global Locations catalogue order.

Parameters:

Name Type Description Default
staypoints Staypoints or dataframe

Staypoints assigned to locations. Raw dataframe-like input is validated through Staypoints.coerce.

required
locations Locations

Global location catalogue linked to the staypoint location_id column.

required

Returns:

Type Description
pandas or Polars DataFrame

Same backend as staypoints with columns location_id_a, location_id_b, and n_people.

Worked example: When and where is each place busy?