T420387: Releasing the growthexperiments-homepage-mentorship-enabled user property¶
Author: Martin Urbanec, Wikimedia Foundation (murbanec@wikimedia.org)
Date: 2026-06-15
NOTE: Public release of this dataset has been approved by WMF Legal (confirmation [access restricted]).
Context¶
In T419002, Yubo Zhou (University of Michigan) requested a dataset showing which users have the Mentorship module available to them. I investigated this request (in an attempt to support the good-faith research) and determined that the growthexperiments-homepage-mentorship-enabled user property fulfils the researcher's needs. The dataset is generated and released under T420387.
The dataset complements the already-public growthmentorship dump. This notebook exists to create the dataset, document it and release it.
The growthexperiments-homepage-mentorship-enabled property¶
This property is auto-generated on signup. It is stored as an integer; the values recognised by the system are:
0: mentorship module disabled by the software (for eg. A/B testing purposes),1(default): mentorship module enabled and visible (provided a mentor can be assigned),50: same meaning as1, but written explicitly after the user was previously in a different state (e.g. they were claimed by a mentor, or opted out and later opted back in); it is interpreted the same as1,2: the user decided to opt out from the mentorship program.
The user can technically store any other value via the API (action=options); such values are not interpreted by MediaWiki in any special way.
Two subtleties matter when reading the data:
- MediaWiki only stores a
user_propertiesrow when the value differs from the property default. The default for this property is1, so users who are enabled by default have no row at all. As a consequence, "row was never written" and "row written as1" are indistinguishable — both surface as the valueunsetbelow, which should be read as enabled (default). - The current value reflects the user's state today, not a historical snapshot at registration. Historical changes are not stored. Mentor claims (publicly logged at Special:Log/growthexperiments) and opt-out/opt-back-in are the main ways a value could differ from the original auto-assignment; both are expected to affect very few accounts.
The growthexperiments-homepage-enabled property¶
This property is auto-generated on signup. It is stored as an integer; the values recognised by the system are:
0(default): homepage is not enabled by the user1: homepage is visible for the user
Similar caveats apply as mentioned above.
Excluded users¶
Based on prior consultations with Trust and Safety, we do not want to make it publicly known which users opted out from mentorship. For this reason the released dataset excludes:
- users who opted out (value
2), and - users whose property is set to any value not recognised by the system (i.e. anything other than
0,1,50, or unset) — these are users who tampered with the property via the API.
Dataset description¶
The released dataset (dataset.tsv, tab-separated) has the following columns:
userId: the user ID assigned by MediaWiki (to allow correlation with other datasets, especially thegrowthmentorshipdump),mentorshipState: the rawgrowthexperiments-homepage-mentorship-enabledvalue — filtered to0,1,50andunset(which means no row, i.e. the default value of enabled).homepageState: the rawgrowthexperiments-homepage-enablevalue — filtered to0,1andunset(which means no row, i.e. the default value of disabled).
The dataset contains users who registered on the English Wikipedia between January 2021 and February 2025 (inclusive) via self-service account creation (isSelfMade), which excludes autocreated, byemail, and create2 accounts — the account types that do not reliably trigger mentor assignment (cf. T419002).
from wmfdata import spark, mariadb
import pandas as pd
import warnings
You are using Wmfdata v2.4.0, but v2.5.0 is available. To update, run `pip install --upgrade git+https://gitlab.wikimedia.org/repos/data-engineering/wmfdata-python.git@release`. To see the changes, refer to https://gitlab.wikimedia.org/repos/data-engineering/wmfdata-python/blob/release/CHANGELOG.md.
def convert_mentorship_enabled(val):
if pd.isna(val):
return 'unset'
val = str(val)
if val in ['0', '1', '50']:
return val
else:
return None
def convert_homepage_enabled(val):
if pd.isna(val):
return 'unset'
val = str(val)
if val in ['0', '1']:
return val
else:
return None
dfUsers = spark.run('''
SELECT
ssac.dt,
ssac.event.userId,
ssac.event.userName,
ssac.event.isSelfMade
FROM event_sanitized.serversideaccountcreation ssac
-- for some reason, certain users were not actually created, join them with the user table of MediaWiki
JOIN wmf_raw.mediawiki_user rmu ON ((rmu.wiki_db = ssac.wiki) AND (rmu.user_id = ssac.event.userId))
WHERE
(
-- between January 2021 and February 2025
(year >= 2021 AND year < 2025)
OR (year = 2025 AND month < 3)
)
AND rmu.snapshot = '2026-05'
AND ssac.wiki = 'enwiki'
AND ssac.event.isSelfMade == true
''')
SPARK_HOME: /srv/home/urbanecm/.conda/envs/2024-06-16T13.49.54_urbanecm/lib/python3.10/site-packages/pyspark Using Hadoop client lib jars at 3.2.0, provided by Spark. PYSPARK_PYTHON=/opt/conda-analytics/bin/python3
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
26/06/15 13:50:08 WARN SparkConf: Note that spark.local.dir will be overridden by the value set by the cluster manager (via SPARK_LOCAL_DIRS in mesos/standalone/kubernetes and LOCAL_DIRS in YARN).
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12000. Attempting port 12001.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12001. Attempting port 12002.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12002. Attempting port 12003.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12003. Attempting port 12004.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12004. Attempting port 12005.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12005. Attempting port 12006.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12006. Attempting port 12007.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12007. Attempting port 12008.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12008. Attempting port 12009.
26/06/15 13:50:08 WARN Utils: Service 'sparkDriver' could not bind on port 12009. Attempting port 12010.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4040. Attempting port 4041.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4041. Attempting port 4042.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4042. Attempting port 4043.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4043. Attempting port 4044.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4044. Attempting port 4045.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4045. Attempting port 4046.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4046. Attempting port 4047.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4047. Attempting port 4048.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4048. Attempting port 4049.
26/06/15 13:50:09 WARN Utils: Service 'SparkUI' could not bind on port 4049. Attempting port 4050.
26/06/15 13:50:12 WARN Client: Exception encountered while connecting to the server : org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.ipc.StandbyException): Operation category READ is not supported in state standby. Visit https://s.apache.org/sbnn-error
26/06/15 13:50:12 WARN Client: Exception encountered while connecting to the server : org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.ipc.StandbyException): Operation category READ is not supported in state standby. Visit https://s.apache.org/sbnn-error
26/06/15 13:50:12 WARN Client: Exception encountered while connecting to the server : org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.ipc.StandbyException): Operation category READ is not supported in state standby. Visit https://s.apache.org/sbnn-error
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13000. Attempting port 13001.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13001. Attempting port 13002.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13002. Attempting port 13003.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13003. Attempting port 13004.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13004. Attempting port 13005.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13005. Attempting port 13006.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13006. Attempting port 13007.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13007. Attempting port 13008.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13008. Attempting port 13009.
26/06/15 13:50:17 WARN Utils: Service 'org.apache.spark.network.netty.NettyBlockTransferService' could not bind on port 13009. Attempting port 13010.
26/06/15 13:50:17 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Attempted to request executors before the AM has registered!
26/06/15 13:50:21 WARN SessionState: METASTORE_FILTER_HOOK will be ignored, since hive.security.authorization.manager is set to instance of HiveAuthorizerFactory.
26/06/15 13:50:38 WARN NioEventLoop: Selector.select() returned prematurely 512 times in a row; rebuilding Selector io.netty.channel.nio.SelectedSelectionKeySetSelector@d1c75e7.
dfs = []
BATCH_SIZE = 10000
for i in range(0, dfUsers.userId.count(), BATCH_SIZE):
batchUsers = dfUsers.userId[i:i + BATCH_SIZE]
with warnings.catch_warnings():
warnings.simplefilter("ignore")
dfTmp = mariadb.run('''
SELECT up_user, up_property, up_value
FROM user_properties
WHERE
up_property IN (
'growthexperiments-homepage-mentorship-enabled',
'growthexperiments-homepage-enable'
)
AND up_user IN ({users})
'''.format(users=','.join(batchUsers.map(str))), 'enwiki')
dfColumnified = (
dfTmp
.pivot(index='up_user', columns='up_property', values='up_value')
.rename(columns={
'growthexperiments-homepage-mentorship-enabled': 'mentorship_enabled',
'growthexperiments-homepage-enable': 'homepage_enabled',
})
.reset_index()
)
dfs.append(dfColumnified)
dfProperties = pd.concat(dfs)
dfProperties.set_index('up_user', inplace=True)
del dfs
df = dfUsers.join(dfProperties, 'userId')
df['mentorshipState'] = df.mentorship_enabled.map(convert_mentorship_enabled)
df['homepageState'] = df.homepage_enabled.map(convert_homepage_enabled)
df = df.loc[(df.mentorshipState.notna()) & (df.homepageState.notna())] # exclude all invalid entries
df.head(10)
| dt | userId | userName | isSelfMade | homepage_enabled | mentorship_enabled | mentorshipState | homepageState | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2021-01-01T01:22:30Z | 40887714 | Rbaker71 | True | NaN | NaN | unset | unset |
| 1 | 2021-01-01T03:01:37Z | 40887953 | InfornmationGladiator | True | NaN | NaN | unset | unset |
| 2 | 2021-01-01T05:05:58Z | 40888258 | Rahul212121 | True | NaN | NaN | unset | unset |
| 3 | 2021-01-01T09:21:25Z | 40888954 | Yasir.ahmed07 | True | NaN | NaN | unset | unset |
| 4 | 2021-01-01T09:30:37Z | 40888987 | Vinod khiradkar | True | NaN | NaN | unset | unset |
| 5 | 2021-01-01T11:13:58Z | 40889338 | Luke4313 | True | NaN | NaN | unset | unset |
| 6 | 2021-01-01T12:46:29Z | 40889659 | Aniruddha S Patil | True | NaN | NaN | unset | unset |
| 7 | 2021-01-01T15:57:55Z | 40890466 | Doo271 | True | NaN | NaN | unset | unset |
| 8 | 2021-01-01T18:16:50Z | 40891093 | Cymru321 | True | NaN | NaN | unset | unset |
| 9 | 2021-01-02T01:38:08Z | 40892701 | Loverrukk 5 | True | NaN | NaN | unset | unset |
dfRelease = df[['userId', 'mentorshipState', 'homepageState']]
dfRelease.to_csv('dataset.tsv', sep='\t', index=False)