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 global location ID.
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.
mean_area_volume Mean user volume per area and 10-minute time bin, averaged across days of the week.
build_stvd Aggregate staypoints against a global Locations catalogue into an STVD frame.

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


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

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


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


fastmob.measures.collective.visits_per_location

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 - like

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
DataFrame or 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).


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

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

fastmob.measures.collective.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 - like

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
DataFrame or 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

fastmob.measures.collective.build_stvd(staypoints, locations, *, location_id_col='location_id')

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

Combines :func: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 :func: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 - like

Staypoints already assigned to locations (e.g. via :meth:Staypoints.generate_global_locations or :meth: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 :meth: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 - like

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