{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[Home](index.ipynb) > [Notebooks](notebooks.ipynb) > Copenhagen Network Study" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ``compsoc`` – Computational Social Methods in Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Copenhagen Networks Study: Multiplex interactions among 845 university students\n", "\n", "**Author**: [Haiko Lietz](https://www.gesis.org/person/haiko.lietz)\n", "\n", "**Affiliation**: [GESIS - Leibniz Institute for the Social Sciences](https://www.gesis.org/), Cologne, Germany\n", "\n", "**Publication date**: XX.XX.XXXX (version 1.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction\n", "\n", "The Copenhagen Networks Study is a beacon in demonstrating the power of a mix of digital behavioral data and survey data as well as mixed methods. In 2013, researchers handed out 1,000 cell phones to students at the Technical University of Denmark and recorded their physical location (via the cell phone’s GPS sensor), who was proximate to whom (via the Bluetooth sensor), who called whom, and who texted whom. In addition, their transactions on Facebook were collected, and their demographic and psychological traits were surveyed. Parts of this data are described by Sapiezynski et al. ([2019](https://doi.org/10.1038/s41597-019-0325-x)). They are made [freely available](https://doi.org/10.6084/m9.figshare.7267433) under an MIT License at figshare. Four types social relations are depicted in Figure 1.\n", "\n", "||\n", "|:--|\n", "|**Figure 1**: Social relations from the Copenhagen Networks Study|\n", "\n", "**In this notebook**, we develop a function that loads and normalizes all data of the Copenhagen Networks Study. Normalizing variables and unifying names makes it compatible with the `compsoc` tools." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dependencies and settings" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import compsoc as cs\n", "import itertools\n", "import numpy as np\n", "import os\n", "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "path = 'data/copenhagen/'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Copenhagen Networks Study interaction data\n", "The dataset contains four types of tie and a user attribute.\n", "\n", "First type of tie: **Network of physical proximity** among the participants (estimated via Bluetooth signal strength)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "bluetooth = pd.read_csv(os.path.join(path, 'bt_symmetric.csv'))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index(['# timestamp', 'user_a', 'user_b', 'rssi'], dtype='object')\n" ] } ], "source": [ "print(bluetooth.columns)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "column names:\n", "\t- timestamp\n", "\t- user A \n", "\t- user B \n", "\t- received signal strength\n", "\n", "Notes:\n", "Empty scans are marked with user B = -1 and RSSI = 0\n", "Scans of devices outside of the experiment are marked with user B = -2. All non-experiment devices are given the same ID.\n", "\n" ] } ], "source": [ "print(open(os.path.join(path, 'bt_symmetric.README'), 'r').read())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Second type of tie: **Network of phone calls** (start time, duration, no content)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "calls = pd.read_csv(os.path.join(path, 'calls.csv'))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index(['timestamp', 'caller', 'callee', 'duration'], dtype='object')\n" ] } ], "source": [ "print(calls.columns)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "column names:\n", "\t- timestamp\n", "\t- calling user\n", "\t- call recipient\n", "\t- duration (-1 means a missed call)\n", "\n", "\n" ] } ], "source": [ "print(open(os.path.join(path, 'calls.README'), 'r').read())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Third type of tie: **Network of text messages** (time of message, no content)." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "sms = pd.read_csv(os.path.join(path, 'sms.csv'))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index(['timestamp', 'sender', 'recipient'], dtype='object')\n" ] } ], "source": [ "print(sms.columns)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "column names:\n", "\t- timestamp\n", "\t- sender\n", "\t- recipient\n", "\n" ] } ], "source": [ "print(open(os.path.join(path, 'sms.README'), 'r').read())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fourth type of tie: **network of Facebook friendships**." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "fb_friends = pd.read_csv(os.path.join(path, 'fb_friends.csv'))" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index(['# user_a', 'user_b'], dtype='object')\n" ] } ], "source": [ "print(fb_friends.columns)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "column names:\n", "\t- user A\n", "\t- user B\n", "\n", "Edge lists of all declared facebook friendships which were formed before the end of the observation and not dissolved until after the end of the observation.\n", "\n" ] } ], "source": [ "print(open(os.path.join(path, 'fb_friends.README'), 'r').read())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "User attribute:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "attributes = pd.read_csv(os.path.join(path, 'genders.csv'))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index(['# user', 'female'], dtype='object')\n" ] } ], "source": [ "print(attributes.columns)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Normalization\n", "The goal of normalization is to bring this data into a standard form which is needed for methods to be applicable.\n", "### ``users`` dataframe\n", "The ``genders`` dataframe is not a complete list of users as some users that participated in relational data collection did not answer the gender question or did not participate in the survey. We must construct a ``users`` dataframe by finding the set of unique users, ..." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "users = set(itertools.chain(*[\n", " bluetooth['user_a'].to_list(), \n", " bluetooth['user_b'].to_list(), \n", " calls['caller'].to_list(), \n", " calls['callee'].to_list(), \n", " sms['sender'].to_list(), \n", " sms['recipient'].to_list(), \n", " fb_friends['# user_a'].to_list(), \n", " fb_friends['user_b'].to_list(), \n", " attributes['# user'].to_list()\n", "]))" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "users = pd.DataFrame(list(users), columns=['user'])\n", "users = users[users['user'] >= 0] # remove artifact users from bluetooth dataframe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... merging user ``attributes`` into it, ..." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "users = pd.merge(left=users, right=attributes, left_on='user', right_on='# user', how='left')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... coding missing gender as 2, ..." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "users.fillna(2, inplace=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... and cleaning up:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "users.rename(columns={'female': 'gender_id'}, inplace=True)\n", "users['gender_id'] = users['gender_id'].astype(int)\n", "users.drop(['# user'], axis=1, inplace=True)\n", "users['user_id'] = users.index\n", "users = users[['user_id', 'user', 'gender_id']]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_idusergender_id
8408408450
8418418460
8428428471
8438438482
8448448502
\n", "
" ], "text/plain": [ " user_id user gender_id\n", "840 840 845 0\n", "841 841 846 0\n", "842 842 847 1\n", "843 843 848 2\n", "844 844 850 2" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "users.tail()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The observation that the largest normalized user identifier (``user_id``) is six numbers shorter than the unnormalized identifier (``user``) means that we avoid dragging along six users for which we do not have any data at all. Since we have created a ``gender_id``, we must also create a dataframe with descriptions of these identifiers:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "genders = pd.DataFrame([[0, 'male'], [1, 'female'], [2, 'unknown']], columns=['gender_id', 'gender'])" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
gender_idgender
00male
11female
22unknown
\n", "
" ], "text/plain": [ " gender_id gender\n", "0 0 male\n", "1 1 female\n", "2 2 unknown" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "genders" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Relations dataframes\n", "#### Bluetooth\n", "The network of physical proximity is an undirected network. The `# timestamp` column is a counter of seconds over four weeks, quantized into bins of five minutes. Within each time bin, all instances of users A and B discovering each other were identified, and the larger signal strength (`rssi`) is reported ([Sapiezynski et al., 2019](https://doi.org/10.1038/s41597-019-0325-x)):" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
# timestampuser_auser_brssi
000-10
101-10
202-10
303-2-88
405-10
\n", "
" ], "text/plain": [ " # timestamp user_a user_b rssi\n", "0 0 0 -1 0\n", "1 0 1 -1 0\n", "2 0 2 -1 0\n", "3 0 3 -2 -88\n", "4 0 5 -1 0" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bluetooth.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These relations need cleaning. Remove ties where no other study participant is found in the bluetooth range ..." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "bluetooth = bluetooth[~bluetooth['user_b'].isin([-1, -2])]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... and remove ties with anomalous signal strengths:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "bluetooth = bluetooth[bluetooth['rssi'] < 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ``# timestamp`` column is a counter of seconds over four weeks. We add a column with a `datetime` type. The information on the year '1970' and the month '01' can be discarded but is necessary for filtering on days, hours, minutes, and seconds:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "bluetooth['time'] = pd.to_datetime(bluetooth['# timestamp'], unit='s')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, we divide the `# timestamp` by 300 to obtain 5 minute time bins:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "bluetooth['time_bin'] = (bluetooth['# timestamp'] / 300).astype(int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `rssi` signal strength is measured in the logarithmic power unit of decibel-milliwatts. The larger the value, the closer two devices are:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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yBYtWqTVq1crNzdXLS0tamlpUW5urrKyshQfHy9JysjI0OLFi+V2u9XR0aHTp09r69atys3NNUdusrOzZbfblZOTo66uLtXW1qq0tJQnxgAAgKQJjgi9/fbbkqTly5cHLH/nnXeUk5MjSdq2bZsGBgaUl5cnr9erlJQUeTwezZ0716zft2+fZs+erQ0bNmhgYEArV65UVVWVgoKCzJojR44oPz/ffLps3bp1qqysNNcHBQXpxIkTysvL07JlyxQaGqrs7GyVlZWZNQ6HQ/X19dq8ebOSk5MVGRmpwsLCgKkvAABgXROeGnsSm82mkpISlZSUPLZmzpw5qqioUEVFxWNr5s2bp+rq6jH39fLLL+v48eNj1iQmJqqhoWHMGgAAYE181hgAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALAsghAAALCsCQehhoYGrV27Vi6XSzabTX/3d38XsN4wDJWUlMjlcik0NFTLly/X+fPnA2p8Pp+2bNmiqKgohYeHa926dbp582ZAjdfrldvtlsPhkMPhkNvt1p07dwJqrl+/rrVr1yo8PFxRUVHKz8/X4OBgQM25c+eUlpam0NBQLViwQDt37pRhGBM9bQAA8AKacBC6f/++/sN/+A+qrKx85Po9e/aovLxclZWVamtrk9PpVHp6uu7evWvWFBQUqLa2VjU1NWpsbNS9e/eUlZWloaEhsyY7O1udnZ2qq6tTXV2dOjs75Xa7zfVDQ0Nas2aN7t+/r8bGRtXU1OjYsWMqKioya/r7+5Weni6Xy6W2tjZVVFSorKxM5eXlEz1tAADwApo90Te88soreuWVVx65zjAM7d+/X9u3b9f69eslSYcOHVJMTIyOHj2qTZs2qa+vTwcPHtThw4e1atUqSVJ1dbViY2N16tQpZWZm6uLFi6qrq1NLS4tSUlIkSQcOHFBqaqouXbqk+Ph4eTweXbhwQTdu3JDL5ZIk7d27Vzk5Odq1a5ciIiJ0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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "bluetooth['rssi'].hist()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is desirable to transform signal strength into metric distance. The [formula](https://beingcoders.9mood.com/formula-to-convert-the-rssi-value-of-the-ble-bluetooth-low-energy-beacons-to-meters-12a2fa426a2e) is $distance = 10^{(p-rssi)/10^n}$ where $p$ is the expected power at a distance of 1 meter and $n$ is the broadcasting power. From Mones et al. ([2017](https://doi.org/10.1140/epjds/s13688-017-0103-y)) we can infer $p=-75$ and $n=2$. With these values we can compute the approximate distance of two devices:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "bluetooth.loc[:, 'distance'] = round(10**((-75 - bluetooth['rssi']) / 10**2), 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After these transformations, the average distance and standard deviation are:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "bluetooth['distance'].hist()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mean: 1.2808650462128153\n", "std: 0.27000182233379405\n" ] } ], "source": [ "print('mean:', bluetooth['distance'].mean())\n", "print('std:', bluetooth['distance'].std())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The edge list still uses the unnormalized node identifiers. To replace them with the normalized ones, we need this dictionary which maps the unnormalized to normalized identifiers:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "map = users.set_index('user').to_dict()['user_id']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use the dictionary for mapping:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "bluetooth['user_id_from'] = bluetooth['user_a'].map(map)\n", "bluetooth['user_id_to'] = bluetooth['user_b'].map(map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now clean up:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "bluetooth.sort_values(['time', 'user_id_from', 'user_id_to'], inplace=True)\n", "bluetooth.reset_index(drop=True, inplace=True)\n", "bluetooth = bluetooth[['user_id_from', 'user_id_to', 'rssi', 'distance', 'time', 'time_bin']]" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_id_fromuser_id_torssidistancetimetime_bin
02120-730.951970-01-010
14719-861.291970-01-010
24948-620.741970-01-010
36944-871.321970-01-010
49064-961.621970-01-010
\n", "
" ], "text/plain": [ " user_id_from user_id_to rssi distance time time_bin\n", "0 21 20 -73 0.95 1970-01-01 0\n", "1 47 19 -86 1.29 1970-01-01 0\n", "2 49 48 -62 0.74 1970-01-01 0\n", "3 69 44 -87 1.32 1970-01-01 0\n", "4 90 64 -96 1.62 1970-01-01 0" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bluetooth.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The edge list for the network of physical proximity is now in the normalized form. The edges are defined in the first two columns, and the other columns can be used for slicing and filtering." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calls\n", "The network of phone calls is directed and encodes who calls whom." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
timestampcallercalleeduration
0184300301121
13920512299670
25623301300504
39252401457-1
41546651205
\n", "
" ], "text/plain": [ " timestamp caller callee duration\n", "0 184 300 301 121\n", "1 3920 512 299 670\n", "2 5623 301 300 504\n", "3 9252 401 457 -1\n", "4 15466 512 0 5" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "calls.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Process the timestamps, re-using the 5-min bins from physical proximity:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "calls['time'] = pd.to_datetime(calls['timestamp'], unit='s')\n", "calls['time_bin'] = (calls['timestamp'] / 300).astype(int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Replace the user identifiers:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "calls['user_id_from'] = calls['caller'].map(map)\n", "calls['user_id_to'] = calls['callee'].map(map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Missed calls are coded as -1. Since negative values cannot be used as edge weights, we recode -1 to 0. Missed calls are then indistinguishable from calls that lasted 0 seconds." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "calls['duration'].replace(-1, 0, inplace=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clean up:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "calls.sort_values(['time', 'user_id_from', 'user_id_to'], inplace=True)\n", "calls.reset_index(drop=True, inplace=True)\n", "calls = calls[['user_id_from', 'user_id_to', 'duration', 'time', 'time_bin']]" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_id_fromuser_id_todurationtimetime_bin
03003011211970-01-01 00:03:040
15122996701970-01-01 01:05:2013
23013005041970-01-01 01:33:4318
340145701970-01-01 02:34:1230
4512051970-01-01 04:17:4651
\n", "
" ], "text/plain": [ " user_id_from user_id_to duration time time_bin\n", "0 300 301 121 1970-01-01 00:03:04 0\n", "1 512 299 670 1970-01-01 01:05:20 13\n", "2 301 300 504 1970-01-01 01:33:43 18\n", "3 401 457 0 1970-01-01 02:34:12 30\n", "4 512 0 5 1970-01-01 04:17:46 51" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "calls.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### SMS\n", "The network of text messages is directed and encodes who sends whom a short message." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
timestampsenderrecipient
018370512
137512370
2126370512
31340512
4312137136
\n", "
" ], "text/plain": [ " timestamp sender recipient\n", "0 18 370 512\n", "1 37 512 370\n", "2 126 370 512\n", "3 134 0 512\n", "4 312 137 136" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sms.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Process the timestamps:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "sms['time'] = pd.to_datetime(sms['timestamp'], unit='s')\n", "sms['time_bin'] = (sms['timestamp'] / 300).astype(int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Replace the user identifiers:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "sms['user_id_from'] = sms['sender'].map(map)\n", "sms['user_id_to'] = sms['recipient'].map(map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clean up:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "sms.sort_values(['time', 'user_id_from', 'user_id_to'], inplace=True)\n", "sms.reset_index(drop=True, inplace=True)\n", "sms = sms[['user_id_from', 'user_id_to', 'time', 'time_bin']]" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_id_fromuser_id_totimetime_bin
03705121970-01-01 00:00:180
15123701970-01-01 00:00:370
23705121970-01-01 00:02:060
305121970-01-01 00:02:140
41371361970-01-01 00:05:121
\n", "
" ], "text/plain": [ " user_id_from user_id_to time time_bin\n", "0 370 512 1970-01-01 00:00:18 0\n", "1 512 370 1970-01-01 00:00:37 0\n", "2 370 512 1970-01-01 00:02:06 0\n", "3 0 512 1970-01-01 00:02:14 0\n", "4 137 136 1970-01-01 00:05:12 1" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sms.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Facebook friendships\n", "The network of Facebook friendships encodes which two users are friends. Since one has to accept a friendship request at Facebook, the tie is undirected." ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
# user_auser_b
00512
10263
20525
30285
40543
\n", "
" ], "text/plain": [ " # user_a user_b\n", "0 0 512\n", "1 0 263\n", "2 0 525\n", "3 0 285\n", "4 0 543" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fb_friends.head()" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "fb_friends['user_id_from'] = fb_friends['# user_a'].map(map)\n", "fb_friends['user_id_to'] = fb_friends['user_b'].map(map)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "fb_friends.sort_values(['user_id_from', 'user_id_to'], inplace=True)\n", "fb_friends.reset_index(drop=True, inplace=True)\n", "fb_friends = fb_friends[['user_id_from', 'user_id_to']]" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_id_fromuser_id_to
00104
10107
20108
30208
40263
\n", "
" ], "text/plain": [ " user_id_from user_id_to\n", "0 0 104\n", "1 0 107\n", "2 0 108\n", "3 0 208\n", "4 0 263" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fb_friends.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Function\n", "This function loads all data in one step:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [], "source": [ "def copenhagen_collection(\n", " path = 'data/copenhagen/'\n", "):\n", " '''\n", " Description: Loads the normalized Copenhagen Networks Study data collection.\n", " \n", " Input:\n", " path: relative directory where the data is; set to 'data/copenhagen/' by default.\n", " \n", " Output: Six dataframes in this order: users, genders, bluetooth, calls, sms, fb_friends\n", " '''\n", " # load data\n", " import os\n", " import pandas as pd\n", " \n", " attributes = pd.read_csv(os.path.join(path, 'genders.csv'))\n", " bluetooth = pd.read_csv(os.path.join(path, 'bt_symmetric.csv'))\n", " calls = pd.read_csv(os.path.join(path, 'calls.csv'))\n", " sms = pd.read_csv(os.path.join(path, 'sms.csv'))\n", " fb_friends = pd.read_csv(os.path.join(path, 'fb_friends.csv'))\n", " \n", " # create users dataframe\n", " import itertools\n", " \n", " users = set(itertools.chain(*[\n", " bluetooth['user_a'].to_list(), \n", " bluetooth['user_b'].to_list(), \n", " calls['caller'].to_list(), \n", " calls['callee'].to_list(), \n", " sms['sender'].to_list(), \n", " sms['recipient'].to_list(), \n", " fb_friends['# user_a'].to_list(), \n", " fb_friends['user_b'].to_list(), \n", " attributes['# user'].to_list()\n", " ]))\n", " users = pd.DataFrame(list(users), columns=['user'])\n", " users = users[users['user'] >= 0]\n", " users = pd.merge(left=users, right=attributes, left_on='user', right_on='# user', how='left')\n", " users.fillna(2, inplace=True)\n", " users.rename(columns={'female': 'gender_id'}, inplace=True)\n", " users['gender_id'] = users['gender_id'].astype(int)\n", " users.drop(['# user'], axis=1, inplace=True)\n", " users['user_id'] = users.index\n", " users = users[['user_id', 'user', 'gender_id']]\n", " map = users.set_index('user').to_dict()['user_id']\n", " \n", " # create genders dataframe\n", " genders = pd.DataFrame([[0, 'male'], [1, 'female'], [2, 'unknown']], columns=['gender_id', 'gender'])\n", " \n", " # create bluetooth dataframe\n", " bluetooth = bluetooth[~bluetooth['user_b'].isin([-1, -2])]\n", " bluetooth = bluetooth[bluetooth['rssi'] < 0]\n", " bluetooth['time'] = pd.to_datetime(bluetooth['# timestamp'], unit='s')\n", " bluetooth['time_bin'] = (bluetooth['# timestamp'] / 300).astype(int)\n", " bluetooth.loc[:, 'distance'] = 10**((-75 - bluetooth['rssi']) / 10**2)\n", " bluetooth['user_id_from'] = bluetooth['user_a'].map(map)\n", " bluetooth['user_id_to'] = bluetooth['user_b'].map(map)\n", " bluetooth.sort_values(['time', 'user_id_from', 'user_id_to'], inplace=True)\n", " bluetooth.reset_index(drop=True, inplace=True)\n", " bluetooth = bluetooth[['user_id_from', 'user_id_to', 'rssi', 'distance', 'time', 'time_bin']]\n", " \n", " # create calls dataframe\n", " calls['time'] = pd.to_datetime(calls['timestamp'], unit='s')\n", " calls['time_bin'] = (calls['timestamp'] / 300).astype(int)\n", " calls['user_id_from'] = calls['caller'].map(map)\n", " calls['user_id_to'] = calls['callee'].map(map)\n", " calls['duration'].replace(-1, 0, inplace=True)\n", " calls.sort_values(['time', 'user_id_from', 'user_id_to'], inplace=True)\n", " calls.reset_index(drop=True, inplace=True)\n", " calls = calls[['user_id_from', 'user_id_to', 'duration', 'time', 'time_bin']]\n", " \n", " # create sms dataframe\n", " sms['time'] = pd.to_datetime(sms['timestamp'], unit='s')\n", " sms['time_bin'] = (sms['timestamp'] / 300).astype(int)\n", " sms['user_id_from'] = sms['sender'].map(map)\n", " sms['user_id_to'] = sms['recipient'].map(map)\n", " sms.sort_values(['time', 'user_id_from', 'user_id_to'], inplace=True)\n", " sms.reset_index(drop=True, inplace=True)\n", " sms = sms[['user_id_from', 'user_id_to', 'time', 'time_bin']]\n", " \n", " # create fb_friends dataframe\n", " fb_friends['user_id_from'] = fb_friends['# user_a'].map(map)\n", " fb_friends['user_id_to'] = fb_friends['user_b'].map(map)\n", " fb_friends.sort_values(['user_id_from', 'user_id_to'], inplace=True)\n", " fb_friends.reset_index(drop=True, inplace=True)\n", " \n", " return users, genders, bluetooth, calls, sms, fb_friends" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using the function\n", "\n", "Load the collection like this:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "users, genders, bluetooth, calls, sms, fb_friends = cs.copenhagen_collection()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## References\n", "\n", "Mones, E., Stopczynski, A., & Lehmann, S. (2017). Contact activity and dynamics of the social core. *EPJ Data Science*, 6, 6. https://doi.org/10.1140/epjds/s13688-017-0103-y\n", "\n", "Sapiezynski, P., Stopczynski, A., Lassen, D. D., & Lehmann, S. (2019). Interaction data from the Copenhagen Networks Study. *Scientific Data*, 6(1), 315. https://doi.org/10.1038/s41597-019-0325-x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "***" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## About this notebook\n", "\n", "**License**: CC BY 4.0. Distribute, remix, adapt, and build upon ``compsoc``, even commercially, as long as you credit us for the original creation.\n", "\n", "**Suggested citation**: Lietz, H. (2025). Copenhagen Networks Study: Multiplex interactions among 845 university students. Version 1.0 (XX.XX.XXXX). *compsoc – Computational Social Methods in Python*. Cologne: GESIS – Leibniz Institute for the Social Sciences. https://github.com/gesiscss/compsoc" ] } ], "metadata": { "kernelspec": { "display_name": "compsoc", "language": "python", "name": "compsoc" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" } }, "nbformat": 4, "nbformat_minor": 4 }