-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
1782 lines (1615 loc) · 102 KB
/
index.html
File metadata and controls
1782 lines (1615 loc) · 102 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
layout: project
title: Reddit Hyperlink Network Project
sections:
- id: cover
title: Cover
- id: preparation
title: Preparation
- id: metric
title: Toxicity metrics
- id: propagation
title: Propagation styles
- id: studycases
title: Case studies
- id: conclusion
title: Conclusion
---
<section id="cover" class="cover-page">
<div class="title">
Epidemiology of Toxicity in the Reddit Hyperlink Network
</div>
<div class="subtitle">
As reports of a mysterious virus known as <strong>toxicity</strong> begin to circulate across the Reddit archipelago, a team
operating under the name <strong>AdAstra</strong> is mandated to investigate the epidemic. Tasked with mapping the terrain,
tracing pathways of propagation, and understanding its unusual spreading patterns, the team answers the call.
This report presents the methods and findings developed to trace the spread of toxicity across the Reddit space.
</div>
<img src="{{ '/assets/img/redditdraw.png' | relative_url }}" alt="reddit draw" class="center map">
<table class="doctors-table">
<tr>
<td>
<img src="{{ '/assets/img/photodoc1.jpg' | relative_url }}" alt="Aksel Acar" class="doctor-photo">
<div class="doctor-name">Aksel Acar</div>
</td>
<td>
<img src="{{ '/assets/img/photodoc2.jpg' | relative_url }}" alt="Emilien Coudurier" class="doctor-photo">
<div class="doctor-name">Emilien Coudurier</div>
</td>
<td>
<img src="{{ '/assets/img/photodoc3.jpg' | relative_url }}" alt="Nathan Tabet" class="doctor-photo">
<div class="doctor-name">Nathan Tabet</div>
</td>
<td>
<img src="{{ '/assets/img/photodoc4.jpg' | relative_url }}" alt="Cyprien Tordo" class="doctor-photo">
<div class="doctor-name">Cyprien Tordo</div>
</td>
<td>
<img src="{{ '/assets/img/photodoc5.jpg' | relative_url }}" alt="Cedric Zanou" class="doctor-photo">
<div class="doctor-name">Cedric Zanou</div>
</td>
</tr>
</table>
<div class="footer">
<div class="class">EPFL CS401 - Applied Data Analysis</div>
<div class="date">December 19, 2025</div>
</div>
</section>
<!-- #################################################################################################################################### -->
<!-- INTRO -->
<!-- #################################################################################################################################### -->
<section id="preparation" class="section-block">
<h1>Preparation</h1>
<!-- #################################################################################################################################### -->
<h3>Context</h3>
<p>
Reddit is often described as a platform or a collection of communities. In our study, we invite you to see it differently: <strong>as a vast archipelago</strong>.
Islands (subreddits) each with their own cultures, norms, and languages, are connected by invisible currents of hyperlinks, references, and shared users.
Most days, these currents carry harmless conversation. But sometimes, something else travels with them... Earlier this year, several monitoring organizations raised an alert: a
mysterious virus has been detected. This virus was not biological, but behavioral: <strong>patterns of toxic behavior appeared to be spreading across Reddit</strong> in ways that
resembled contagion rather than coincidence. Hostile language, harassment, and inflammatory content were no longer confined to isolated communities. Multiple variants
seemed to have emerged, spreading in atypical ways across interconnected communities and making the dynamics of the epidemic unusually complex.
</p>
<p>
To investigate this digital virus, the World Health Organization (WHO) mandated our team of epidemiologists equiped with a Minor in data analysis: <strong>AdAstra</strong>.
Our goal is not to moralize or moderate, but to observe, map, and understand the investigate the epidemiology of toxicity in Reddit’s hyperlink network.
Specifically, this study seeks to answer three key questions:
</p>
<ol>
<li>
<strong>Variant characterization: </strong>Can we identify and clearly define the different variants of toxicity circulating in the Reddit archipelago?
</li>
<li>
<strong>Community mapping: </strong>How do these variants manifest across different types of communities, and are certain subreddits particularly associated
with specific types of toxicity?
</li>
<li>
<strong>Inter-community influence: </strong>Through selected case studies, can we trace how individual communities influence each other and understand potential
causal pathways in the spread of toxic behavior?
</li>
</ol>
<p>
This notebook presents our
research and results as we navigate the Reddit archipelago, examine clusters of related communities, and track how toxic behavior emerges and
propagates through the network.
</p>
<div class="box-note box-collapsible">
<div class="box-header">
<div>
<strong>Note - </strong>If you actually don't know what Reddit is...
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<img src="{{ '/assets/img/redditlogo.png' | relative_url }}" alt="Reddit Logo" class="center small">
<p>
<strong>Reddit</strong> is a large, community-driven social media platform <strong>structured around user-created forums known as subreddits.</strong>
Each subreddit centers on a specific topic (ranging from news, politics, and science to niche hobbies and pop-culture
interests) and functions as its own semi-autonomous community with distinct norms, moderators, and posting cultures.
Interactions on Reddit occur primarily through posts and comments, and one subreddit can reference another by including
a hyperlink within the text or title of a post. These cross-subreddit links form what is known as the <strong>Reddit Hyperlink
Network</strong>: a web of directed connections that reveals how information, discussions, and community attention flow across the
platform.
</p>
</div>
</div>
<p>
</p>
<!-- #################################################################################################################################### -->
<h3>Our Tools for the Mission</h3>
<p>
As part of our mission, the WHO provided us with detailed records of interactions between communities across the Reddit archipelago.
Unable to visit the islands directly due to the risk of contagion, we rely on these essential tools to investigate the virus
remotely and effectively. Specifically, we make use of the following three datasets:
</p>
<ul>
<li><strong>Reddit Hyperlink Network (Post Bodies): </strong>A directed record of how subreddits reference one another through hyperlinks embedded in
post content, annotated with time, sentiment classification, and 86 textual features that allow us to track toxic interactions as they occur.</li>
<li><strong>Reddit Hyperlink Network (Post Titles): </strong>A complementary dataset capturing the same type of interactions, but originating from post titles</li>
<li><strong>Subreddit Embeddings: </strong>A set of 300-dimensional semantic representations for each subreddit, encoding thematic similarity and latent relationships between communities beyond explicit links.</li>
</ul>
<div class="box-note box-collapsible">
<div class="box-header">
<div>
<strong>Note - </strong>If you want a bit more details on the datasets' structures...
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<h4>Reddit Hyperlink Network Datasets</h4>
<p>
<a href="http://snap.stanford.edu/data/soc-RedditHyperlinks.html"
target="_blank"
rel="noopener noreferrer"
style="color: #003366; font-weight: 500; text-decoration: underline;">
Source
</a>
</p>
<p>
The Reddit Hyperlink Network models interactions between subreddits through hyperlinks embedded in Reddit posts.
Two complementary datasets are used: one dataset containing hyperlinks appearing in the
body of posts, and one dataset containing hyperlinks appearing in post titles.
</p>
<p>
Both datasets share the same structure. Each row corresponds to a directed interaction from a source subreddit to a
target subreddit at a specific point in time. In addition to the subreddit identifiers, each interaction includes a timestamp,
a binary sentiment label (±1), and 86 properties describing the textual content from which the hyperlink originates.
</p>
<p>
In the context of this project, the focus is on the toxicity of the message, regardless of whether the hyperlink appears in the
body or the title of a post. As a result, the two datasets are concatenated into a single dataframe and then reordered
chronologically.
</p>
<h4>Subreddits Embeddings</h4>
<p>
<a href="https://snap.stanford.edu/data/web-RedditEmbeddings.html"
target="_blank"
rel="noopener noreferrer"
style="color: #003366; font-weight: 500; text-decoration: underline;">
Source
</a>
</p>
<p>
This project also uses precomputed subreddit embeddings to incorporate semantic
information into the analysis. Each row in this dataset represents a subreddit encoded as a dense numerical vector
of dimension 300. These embeddings capture latent semantic relationships between subreddits learned from Reddit
activity and provide a complementary perspective to the explicit hyperlink network.
</p>
</div>
</div>
<p>
Before tracing any outbreak, we needed to understand the terrain. Using the subreddit embeddings, we immediately projected Reddit into a two-dimensional space,
transforming an abstract network into a navigable landscape of clustered islands and thematic regions (subreddits with less than 20 total interactions
over the period were discarded). Doing so, we were able to produce the Reddit map that
guided us throughout our study. For the first time, the scale of the challenge became visible...
</p>
<iframe src="assets/interactives/reddit_maps/reddit_map_topic.html" width="100%" height = "630px" style="border: none;"></iframe>
<!-- #################################################################################################################################### -->
<h3>Methodology overview</h3>
<div class="methodology-overview">
<ol>
<li><strong>Designing a metric to detect and quantify toxicity</strong>
<ul>
<li>Toxicity scoring using linguistic features</li>
<li>Toxicity binary classification, combining score and sentiment</li>
</ul>
</li>
<li>Classifying different toxicity propagation styles
<ul>
<li>Selectioning features characterizing toxicity propagation</li>
<li>Identify different toxicity propagation styles (clustering)</li>
<li>Inject clusters onto the Reddit embeddings map</li>
</ul>
</li>
<li>In-depth case studies - Focus on restrained time-windows and limited subreddits to study
<ul>
<li>Study case 1: "bettersubredditdrama"</li>
<li>Study case 2: "science"</li>
<li>Study case 3: "drama"</li>
</ul>
</li>
</ol>
</div>
</section>
<!-- #################################################################################################################################### -->
<!-- METRICS -->
<!-- #################################################################################################################################### -->
<section id="metric" class="section-block">
<h1>Toxicity measurements</h1>
<p>
Before any outbreak could be tracked, one critical problem had to be solved: raw data alone cannot diagnose an epidemic.
To study propagation, we first needed a reliable and consistent way to detect and quantify toxicity at its source: the individual Reddit post.
</p>
<p>
We define toxicity as <strong>patterns of communication or behavior that create a hostile, disrespectful, or harmful environment for
participants</strong>. This can include insults, harassment, hate speech, excessive negativity, or attempts to provoke conflict.
Toxicity reduces constructive discussion, discourages participation, and can damage the overall health and cohesion of the community.
</p>
<!-- #################################################################################################################################### -->
<h3>Exploiting the 86 textual properties</h3>
<p>
Our dataset includes 86 textual properties covering structural, lexical, emotional, and LIWC-derived indicators.
While these features were not originally designed specifically for toxicity detection, they collectively
capture many linguistic correlates of antagonistic or degrading speech.
</p>
<div class="box-remark box-collapsible">
<div class="box-header">
<div>
<strong>Remark - </strong>Learn more about what the 86 features capture:
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p>
<strong>Properties 1 to 18</strong> capture surface-level linguistic and structures of the text. They measure the basic composition
and readability of a message, including its length, complexity, and word or sentence structure. They include metrics such as
character and word counts, fractions of different character types (letters, digits, punctuation, etc.), sentence length averages, and
readability scores. Together, they provide a quantitative snapshot of how the text is written rather than what it expresses.
</p>
<p>
<strong>Properties 19 to 21</strong> represent sentiment analysis metrics, derived using the VADER (Valence Aware Dictionary and
sEntiment Reasoner) model. They aim to measure the emotional polarity of the text. It includes the Positive Sentiment, Negative Sentiment
and Compound Sentiment. These features reflect the emotional valence of the language used.
</p>
<p>
<strong>Properties 22 to 86</strong> are purely lexicon-based ratios, derived using a text analyis tool called Linguistic Inquiry and
Word Count (LIWC). Each LIWC represents the proportion of words in a text that belong to a certain lexicon: <code>LIWC_X = (nb words in lexicon X)
/ (total nb words)</code>. While useful to see the presence of a certain vocubulary in a post, they remain purely lexical and don't treat emotion
or sentiment in any way.
</p>
</div>
</div>
<p>
Below are some metrics that are promising to use, based on our qualitative defintion of toxicity.
</p>
<table class="toxicity-table">
<thead>
<tr>
<th>Dimension</th>
<th>Description</th>
<th>Example of properties to use</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Negative</strong></td>
<td>Measures strength of negative emotion</td>
<td>
<code>Neg_Vader</code>, <code>LIWC_Negemo</code>,
<code>LIWC_Anger</code>, <code>LIWC_Anx</code>…
</td>
</tr>
<tr>
<td><strong>Aggressive</strong></td>
<td>Captures hostile or taboo language</td>
<td>
<code>LIWC_Swear</code>, <code>LIWC_Sexual</code>,
<code>LIWC_Death</code>, <code>LIWC_Relig</code>…
</td>
</tr>
<tr>
<td><strong>Targeting</strong></td>
<td>Focus on others (potential hostility toward people/groups)</td>
<td>
<code>LIWC_You</code>, <code>LIWC_Humans</code>,
<code>LIWC_Social</code>…
</td>
</tr>
<tr>
<td><strong>Not cognitive</strong></td>
<td>Opposite of reasoned or analytical tone</td>
<td>
<code>LIWC_Insight</code>, <code>LIWC_Cause</code>…
</td>
</tr>
<tr>
<td><strong>Violent and short</strong></td>
<td>Style indicators of impulsive or aggressive speech</td>
<td>
<code>Number of UPPERCASE words</code>,
<code>Readability index</code>…
</td>
</tr>
</tbody>
</table>
<div class="box-error box-collapsible">
<div class="box-header">
<div>
But most importantly, we consider real <strong>emotional negativity</strong> of a post to be a <strong>necessary</strong>
component for it to be toxic. None of these 86 properties actually measure it...
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p>
While the VADER scores seemed promising (optimized for social media texts and posts), they mainly work like a LIWC score
and calculate emotion of the <strong>language</strong> that was used (like an advanced lexical repertory of words that are conventionally
considered negative or harmful on social platforms). This cannot be considered an accurate measure of negativity in our context.
For example, some shortfalls of lexical metrics include:
</p>
<ul>
<li><u>Sarcasm, irony, and context</u>: Commenting “Stupid guy” in a link targeting a funny video of a guy slipping or in a link targeting a video of
a political speech doesn't embody the same negativity.</li>
<li><u>Cultural difference in language</u>: Some communities are (by culture) founded on “offensive” language without being negative.
For example, a <em>CallOfDuty</em> subreddit will frequently mention terms like "kill" or “assassination”.
These words are not toxic in that community, but could be considered so in other subreddits (ones not related to war or gaming). </li>
</ul>
<p>
Because toxicity in our definition must reflect true negative intent, not just negative words, lexical sentiment proves insufficient.
</p>
</div>
</div>
<!-- #################################################################################################################################### -->
<h3>Exploiting sentiment classification</h3>
<p>
<code>LINK_SENTIMENT</code> is derived using a supervised sentiment classifier trained on manually labeled Reddit data.
When labeling, the authors have taken into account their human judgement of context, irony etc. in addition of a purely lexical analysis.
So this binary sentiment classifier is designed specifically for inter-subreddit interactions and is our most reliable (categorical)
measure for pure negativity.
</p>
<p>
To understand which properties meaningfully contribute to negative interactions, <strong> we compare normalized mean property values between positive and
negative links</strong>. Comparing group means detects systematic differences in linguistic patterns (and normalization accounts for differing scales
and distributions across features). We naturally expect toxicity-related linguistic features to cluster more strongly in negative posts.
Identifying features more common in negative interactions ensures alignment with our conceptual model (toxicity requires negativity).
</p>
<div class="box-remark box-collapsible">
<div class="box-header">
<div>
<strong>Remark - </strong>We select a z-difference threshold of 0.25, which retains ~20 properties. Why?
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<ul>
<li>A difference of <strong>0.25 standard deviations</strong> corresponds to a small-to-moderate effect size.
In textual and psychological data, small-to-moderate effects are often meaningful because language patterns
are inherently noisy and multidimensional. A 0.25 cutoff ensures that <strong>weak or negligible features are excluded</strong>,
while <strong>features with clear group differences in positive vs negative sentiment are retained</strong>. This balances
strictness with comprehensiveness.</li>
<li>As we explained above in our definition of toxicity, it cannot be captured by a single marker but arises from a
constellation of linguistic behaviors. Well-validated toxicity models (Google Perspective API, Jigsaw toxicity challenges...)
incorporate dozens of features, not just a few. On the other side, if the metric relied on too many features, it would
risk misinterpreting domain-specific vocabulary as toxic and producing high-variance estimates for minority communities.
Using ~20 features distributes the metric across a reasonably broad linguistic base, improving stability and most importantly
cross-subreddit generalizability.</li>
<li>20 features is consistent with dimensionality in psycholinguistic instruments. Psycholinguistic tools like Empath (200+ categories)
or Biber's Dimensions of Register Variation (50+ linguistic dimensions) show that meaningful linguistic traits require between 10 and
30 variables to capture reliably.</li>
</ul>
<p>
To summarize these arguments, retaining <strong>20 properties</strong> maximizes informational coverage, ensures robustness across heterogeneous subreddits,
matches standard dimensional expectations from psycholinguistics, and corresponds to a statistically meaningful effect-size cutoff.
The threshold of <strong>0.25</strong> therefore strikes the optimal balance between inclusiveness and discriminative power.
</p>
</div>
</div>
<p>
The figure highlights how each properties differ between positive and negative links after z-score normalization, emphasizing features with the largest contrasts.
Hovering reveals the underlying property names and quantitative differences.
</p>
<iframe src="assets/interactives/property_diffs.html" width="100%" height = "520px" style="border: none;"></iframe>
<!-- #################################################################################################################################### -->
<h3>Toxicity scoring</h3>
<p>
Based on the conceptual qualitative choice of what defines toxicity and the empirical negativity analysis above, we <strong>select a subset of properties</strong>
to constitute our toxicity metric and build a <strong>weighted average</strong> of them. These properties already exist in ratio form (0–1), ensuring comparability.
</p>
<div class="box-warning box-collapsible">
<div class="box-header">
<div>
<code>TOXICITY_SCORE</code>: Continuous metric that measures the toxicity intensity in a subreddit post.
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p>
Scoring is defined as a <strong>weighted linear combination of the selected properties</strong>. A linear model is appropriate because it balances interpretability (and will
allow immediate examination of feature contributions) with simple comparison across subreddits.
It also supports later scaling, standardization, and model-based refinements. Weights are based on informed judgments from literature on aggressive discourse
(e.g., strong weight on anger and swearing; negative weight on cognitive complexity features like insight or cause).
</p>
<p>
<strong>Weights are normalized by the sum of absolute values.</strong> This choice preserves relative influence of features and rescales the score into a bounded and
interpretable range. Il also ensures no single feature arbitrarily dominates due to coefficient magnitude. This normalization is a standard approach for
interpretability when working with heterogeneous features and manually chosen weights.
</p>
</div>
</div>
<p>
The following table presents the selected properties to build the toxicity score, with their linear weights. The figure visualizes the relative importance of
selected properties in the toxicity scoring;
slice size reflects coefficient magnitude while color encodes sign.
Hovering over the slices reveals the exact feature name and weight.
</p>
<div class="split-container">
<!-- Partie gauche: tableau -->
<div class="split-left">
<table class="property-table">
<thead>
<tr>
<th>Property</th>
<th>Weight</th>
</tr>
</thead>
<tbody>
<!-- Positive weights (red), descending order -->
<tr><td>NEGATIVE_SENTIMENT_CALCULATED_BY_VADER</td><td style="color:red;">5</td></tr>
<code>
<tr><td>LIWC_SWEAR</td><td style="color:red;">5</td></tr>
<tr><td>LIWC_ANGER</td><td style="color:red;">5</td></tr>
<tr><td>FRACTION_OF_STOPWORDS</td><td style="color:red;">1</td></tr>
<tr><td>LIWC_SOCIAL</td><td style="color:red;">1</td></tr>
<tr><td>LIWC_HUMANS</td><td style="color:red;">1</td></tr>
<tr><td>LIWC_NEGEMO</td><td style="color:red;">1</td></tr>
<tr><td>LIWC_SEXUAL</td><td style="color:red;">1</td></tr>
<!-- Negative weights (green) -->
<tr><td>LIWC_CAUSE</td><td style="color:green;">-5</td></tr>
<tr><td>LIWC_INSIGHT</td><td style="color:green;">-5</td></tr>
<tr><td>FRACTION_OF_DIGITS</td><td style="color:green;">-3</td></tr>
</code>
</tbody>
</table>
</div>
<!-- Partie droite: camembert -->
<div class="split-right">
<iframe src="{{ '/assets/interactives/weights_slices.html' | relative_url }}"
class="interactive-camembert">
</iframe>
</div>
</div>
<!-- #################################################################################################################################### -->
<h3>Toxicity binary classification</h3>
<p>
We now have a reliable, continuous measure of toxicity. But it's not necessarily easy to interpret directly...
We would like to accompany this continuous metric with a binary classification: "Is this toxicity score value sufficiently high?
In other words, is this message toxic?" To answer this question, we design a binary classification by passing a threshold on <code>TOXICITY_SCORE</code>.
Specifically, the threshod above which messages are flagged as toxic is <strong>the quantile-75 of the toxicity scores distribution</strong>. That is, we
mark as significantly toxic the top 25% messages with highest score. We also enforce that only messages with negative sentiment can be classified as toxic.
</p>
<div class="box-warning box-collapsible">
<div class="box-header">
<div>
<code>TOXICITY_CAT</code>: Categorical metric that classifies posts as non-toxic (0) or toxic (1).
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p>
Why is the <strong></strong>quantile-75</strong> of the distribution interpretable and consistent?
</p>
<ul>
<li>Toxic behavior is statistically rare — and tail-based thresholds reflect this. Online toxicity is considered to be heavy-tailed in
the sense that most posts are neutral, while toxic ones are relatively rare and concentrated at the upper end of the distribution.
Selecting the <strong>upper quartile (top 25%)</strong> isolates the <strong>long tail of intense language</strong> but ensures coverage of sufficiently
frequent abusive content. Thus, the 75th percentile is a natural and theoretically grounded boundary.</li>
<li>75th percentile ensures class balance suitable for downstream modeling. Binary classification tasks require a <strong>non-trivial positive class</strong> to avoid degenerate models.
Cutting at 90% would lead to the positive class being too small (5–10%), sparse, with high variance. Cutting at 50% would lead to the positive class being too large,
not aligned with toxicity rarity.</li>
<li>The 75th percentile is conceptually aligned with “significant toxicity”. By definition, we want to classify only <strong>strongly toxic</strong> posts as toxic.
Choosing the <strong>top quartile</strong> is a reliable way to identify posts with unusually high use of aggressive markers and atypical linguistic patterns.
This makes toxicity classification interpretable and consistent across subreddits.</li>
</ul>
<p>
Using only the threshold-based classification, we obtain the following cross-counts:
</p>
<img src="{{ '/assets/img/metriccat.png' | relative_url }}" alt="cat before adj" class="center medium">
<p>
At first, it appears that the majority of toxic messages are actualy labeled positive, which is not desirable as we explicited that,
in our context, toxicity only comes with negativity. However, looking at the <strong>proportions</strong> of toxicity in each sentiment class (right barplot),
we see that around 78% of negative messages are labeled toxic, while only 19% of positive messages were.
Negative messages are much more likely to be toxic (which makes sense based on the properties we retained to define toxicity).
</p>
<p>
To finalize our binary classification, we enforce the rule that only messages with negative sentiment (<code>LINK_SENTIMENT = −1</code>) can be classified
as toxic (<code>TOXICITY_CAT = 1</code>). This decision is based on a conceptual definition of toxicity as hostile, harmful,
or abusive discourse, which fundamentally requires a negative emotional valence. Although positive/neutral-sentiment messages may
sometimes contain high levels of features like swearing or anger, classifying them as toxic would introduce significant false positives that
do not align with the standard understanding of online harassment or abuse.
</p>
<img src="{{ '/assets/img/metriccat2.png' | relative_url }}" alt="cat after adj" class="center medium">
</div>
</div>
<p>
Note that the continuous <code>TOXICITY_SCORE</code> is preserved even for the links that are ultimately filtered out by the sentiment check. This score serves as a
crucial linguistic feature intensity metric, quantifying the sheer presence of aggressive or low-quality language features (e.g., swearing, anger, simplicity)
independent of the link's overall "true" sentiment. By keeping the raw score, we retain valuable information for later analysis, such as comparing the
distribution of strong language features across both toxic (negative sentiment) and non-toxic (positive sentiment) messages, which can reveal linguistic
patterns related to intense discourse beyond just negative intent.
</p>
<div class="box-success">
<strong>Great!</strong> We succesfully designed a reliable continuous metric to measure toxicity intensity in a message, as well as
a binary classifier that distinguishes significantly toxic messages.
</div>
<p>
Our team could move beyond theory and into observation:
we returned to our initial map of the Reddit archipelago and colored each island (subreddit) according to its toxic interactions count over time.
</p>
<iframe src="assets/interactives/reddit_maps/monthly_evolution.html" width="100%" height = "640px" style="border: none;"></iframe>
<p>
This map gave us a first overview on the dynamics of the epidemic: pockets of intensity appear, gradients form across regions, and clusters seem to pulse with activity.
And yet, patterns are not clearly emerging yet... While the map confirms that something was happening, it doesn't really <strong>explain</strong> anything yet.
We needed to go beyond raw intensity and focus on structure. Our next step is clear: to identify and cluster the different variants of toxicity propagation
shaping the Reddit space.
</p>
</section>
<!-- #################################################################################################################################### -->
<!-- PROPAGATION CLUSTERING -->
<!-- #################################################################################################################################### -->
<section id="propagation" class="section-block">
<h1>Propagation styles</h1>
<h3>Clustering toxicity propagation to identify variants</h3>
<p>
As the investigation progressed, it became clear that the virus did not spread in a single, uniform
way. Much like a pathogen with multiple strains, toxicity exhibited distinct temporal and behavioral
patterns across the Reddit archipelago. Some communities acted as chronic carriers, others showed
sudden outbreaks, while some primarily received or reciprocated toxic interactions.
</p>
<p>
To identify these variants, our team selected a set of features capturing toxicity over time,
incoming and outgoing toxicity, and the intensity and reach of interactions between communities. By
clustering subreddits based on these indicators, we uncovered <strong>four distinct propagation styles</strong>, each
representing a different “variant” in how toxicity emerges, persists, and spreads across the network.
</p>
<div class="box-remark box-collapsible">
<div class="box-header">
<div>
<strong>Remark - </strong> What are the selected features used for clustering?
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p><strong>Source (agressor) temporal features</strong></p>
<ul class = "ulpack">
<li><code>src_tox_count</code>: Total number of toxic interactions sent.</li>
<li><code>src_active_span_days</code>: Duration between the first and last toxic interaction sent.</li>
<li><code>src_median_gap_days</code>: Median time gap between consecutive sent toxic interactions.</li>
<li><code>src_gap_cv</code>: Variability of time gaps between sent toxic events.</li>
<li><code>src_max_30d_count</code>: Maximum number of toxic interactions within any 30-day window.</li>
<li><code>src_event_rate</code>: Average rate of sent toxic interactions over the active period.</li>
<li><code>src_pct_events_recent</code>: Proportion of sent toxic interactions occurring in the most recent period.</li>
</ul>
<p><strong>Target (victim) temporal features</strong></p>
<ul class = "ulpack">
<li><code>tgt_tox_count</code>: Total number of toxic interactions received.</li>
<li><code>tgt_active_span_days</code>: Duration between the first and last toxic interaction received.</li>
<li><code>tgt_median_gap_days</code>: Median time gap between consecutive received toxic interactions.</li>
<li><code>tgt_gap_cv</code>: Variability of time gaps between received toxic events.</li>
<li><code>tgt_pct_events_recent</code>: Proportion of received toxic interactions occurring in the most recent period.</li>
</ul>
<p>
These temporal features capture differences between sustained and episodic behavior, regular
versus bursty activity, and increasing or declining toxicity over time.
</p>
<p><strong>Network features</strong></p>
<ul class = "ulpack">
<li><code>net_out_degree</code>: Number of distinct subreddits targeted by toxic interactions.</li>
<li><code>net_in_degree</code>: Number of distinct subreddits sending toxic interactions.</li>
<li><code>net_reciprocity</code>: Proportion of bidirectional toxic relationships.</li>
</ul>
<p>
Network features describe how subreddits are embedded in the toxicity network and help
distinguish isolated aggressors, frequent targets, reciprocal conflicts, and highly connected
communities.
</p>
<p>
Together, these features provide a multidimensional representation of toxic behavior, enabling
clustering based on behavioral patterns rather than simple toxicity volume.
</p>
</div>
</div>
<h4>Clustering Preprocessing</h4>
<p>
Before we could catalogue “variants” of toxicity, we had to do what any careful medical unit would do: discard unreliable
samples and calibrate every instrument. Not every island has the same exposure: some see a handful of toxic encounters, others
are ports where toxicity arrives daily. If we cluster raw measurements, K-Means mostly rediscovers size. So we preprocess to reveal
behavioral variants, how toxicity persists, spikes, and travels. We apply four essentials: filter sparse islands, clean edge cases,
tame outliers, and standardize units.
</p>
<div class="box-error box-collapsible">
<div class="box-header">
<div>
<strong>Preprocessing essentials</strong>:
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<ul>
<li><strong>Low-activity filter</strong>: keep only subreddits with at least 10 toxic interactions.<br>This avoids unstable temporal statistics (e.g., gap variability from 2–3 events).</li>
<li><strong>Log compression</strong> (heavy-tailed counts/spans/degrees): X' = log(1 + X). <br>This reduces the dominance of a few very large
communities in Euclidean distances and PCA.</li>
<li><strong>Winsorization</strong> (bounded percentages): cap extreme values to limit leverage.<br>Useful for ratio-like features (“% recent”)
where a some edge cases can skew centroids.</li>
<li><strong>Mega flag</strong>: mark the top 1% on raw activity/degree as <code>is_mega</code>.<br>We keep mega-subreddits in clustering,
but track them explicitly for interpretation.</li>
<li><strong>Standard scaling</strong>: (X − µ) / σ so units contribute comparably.<br>K-Means and PCA are distance/variance-driven so
scaling prevents “days” or “counts” from winning by default.</li>
</ul>
</div>
</div>
<h4>Reduction through PCA</h4>
<p>
Even with redundant symptoms removed, we're still tracking 14 different behavioral indicators per
community. That's a lot of data to process when trying to identify distinct virus variants.
Principal Component Analysis (PCA) helps us lower the 14 measurements down to the core patterns
that really matter. We are trying to identify key symptoms that define a disease: instead of tracking
every minor detail, we focus on the fundamental signatures that distinguish one variant from
another.
</p>
<p>
We keep enough components to capture 80% of the behavioral variance, basically retaining the most
important diagnostic information while filtering out noise. This gave us 6 core "behavioral markers"
that efficiently represent how different communities engage with toxic behavior, making variant
classification clearer and more reliable.
</p>
<h4>Clustering with KMeans</h4>
<p>
With our refined behavioral profiles ready, we can finally classify communities into distinct toxicity
variants. We use K-Means clustering, an algorithm that groups communities with similar behavioral patterns
together. K-Means works well here because it efficiently handles our continuous measurements and
produces clear, interpretable variant types.
</p>
<div class="box-remark box-collapsible">
<div class="box-header">
<div>
<strong>Remark - </strong>How do we determine the optimal K?
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p><strong>The key challenge: determining how many distinct variants exist in our data.</strong></p>
<p>How many toxicity variants are circulating in the Reddit archipelago? Too few categories and we'll
miss important distinctions between different strains. Too many and we'll see differences where
none truly exist.</p>
<p>
We test different numbers of variants (K = 2 through 10) using two diagnostic measures:
</p>
<ul>
<li><strong>Silhouette score: </strong> Measures how well each community fits its assigned variant compared to other
variants. For each community <i>i</i> in variant <i>C<sub>i</sub></i>:
<p style="text-align: center;">
<i>s(i)</i> = (<i>b(i)</i> - <i>a(i)</i>) / max(<i>a(i)</i>, <i>b(i)</i>)
</p>
where <i>a(i)</i> is the average distance to other communities in the same variant (cohesion), and <i>b(i)</i> is
the average distance to the nearest other variant (separation). Scores range from -1 to +1, with higher values
indicating clearer, more distinct behavioral types.
</li>
<li><strong>Balance: </strong> Measures how evenly communities are distributed across variants, calculated as the ratio of
smallest to largest variant size. A balance near 1 means roughly equal variant populations; near 0
means one dominant "catch-all" group.
</li>
</ul>
<p>
We also run <strong>100 stability tests</strong> for each K value using the Adjusted Rand Index (ARI) to ensure variant classifications are consistent
and reproducible.
</p>
<p>
After systematic evaluation, </strong>K = 4</strong> emerges as optimal: high clustering
quality (silhouette ≈ 0.4), reasonable balance, and excellent stability (94% consistency across runs). Four distinct toxicity
variants, each representing a genuine behavioral archetype.
</p>
</div>
</div>
<h4>Confirming the Variants (Cluster Validation)</h4>
<p>
Once clusters emerge, we treat them like a first diagnosis: plausible, but not yet proven. We validate from two angles:
geometry (are clusters compact and separated?) and evidence (do clusters truly differ on the features?).
</p>
<div class="box-remark box-collapsible">
<div class="box-header">
<div>
<strong>Remark - </strong>Confirming clusters through <strong>validation metrics</strong> (DB, CH, Kruskal–Wallis):
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<ul>
<li>Davies–Bouldin (DB): lower is better; penalizes overlap (high within-cluster spread vs. small separation).
<br>A “worst-neighbor” style check: it highlights if any two clusters are hard to tell apart.</li>
<li>Calinski–Harabasz (CH): higher is better; rewards strong between-cluster variance relative to within-cluster variance.
<br>A global signal of whether clustering explains meaningful structure (not just noise).</li>
<li>Kruskal-Wallis H-test: non-parametric feature-by-feature test; checks whether clusters have different distributions.
<br>We apply a conservative multiple-testing correction (Bonferroni-style) and focus on the most discriminative features.</li>
</ul>
</div>
</div>
<h4>Results and Interpretation</h4>
<p>
It is now clear that the virus doesn't spread uniformly across the Reddit archipelago.
Instead, it manifests through a limited number of recurring patterns which are distinct variants defined by how
communities interact, over what time scales, and in which roles. This analysis revealed four distinct behavioral
archetypes of toxic behavior, each characterized by its <strong>activity level</strong>, <strong>dominant role</strong>
(aggressor or victim), and <strong>temporal dynamics</strong>.
</p>
</div>
</div>
<table class="propagation-cluster-table">
<thead>
<tr>
<th>Cluster</th>
<th>Characterization</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr>
<td class="cluster-name" style="color:#d62728;">Cluster 0</td>
<td>Moderate-volume Agressor - Mixed</td>
<td>228 (including 1 Mega)</td>
</tr>
<tr>
<td class="cluster-name" style="color:#579cf7;">Cluster 1</td>
<td>Moderate-volume Victim-leaning - Mixed</td>
<td>636 (0 Mega)</td>
</tr>
<tr>
<td class="cluster-name" style="color:#2ca02c;">Cluster 2</td>
<td>High-volume Balanced (Hub) - Bursty</td>
<td>302 (including 24 Mega) </td>
</tr>
<tr>
<td class="cluster-name" style="color:#ff7f0e;">Cluster 3</td>
<td>Low-volume Victim - Mixed</td>
<td>198 (including 1 Mega)</td>
</tr>
</tbody>
</table>
<p>
</p>
<ul>
<li>
<strong><span style="color: #d62728;">Cluster 0</span>: Moderate-volume Aggressor – Mixed</strong> <br>
These subreddits primarily send toxic messages but do so irregularly. They target multiple communities while rarely
being targeted themselves. <br>
<u>Interpretation:</u> periodic aggressors, often reacting to specific events rather than sustaining
long-term campaigns.
</li>
<li>
<strong><span style="color: #579cf7;">Cluster 1</span>: Moderate-volume Victim-leaning – Mixed</strong> <br>
These communities receive more toxicity than they send, but still engage in retaliatory behavior. <br>
<u>Interpretation:</u> controversial communities involved in ongoing conflicts, more often attacked than attacking, but not
passive.
</li>
<li>
<strong><span style="color: #2ca02c;">Cluster 2</span>: High-volume Balanced (Hub) – Bursty</strong> <br>
Highly active communities with intense, reciprocal toxic exchanges occurring in short bursts. <br>
<u>Interpretation:</u> central hubs where major conflicts erupt, often triggered by external events.
The bursty behavior is explained as this cluster contains most Mega-subreddits.
</li>
<li>
<strong><span style="color: #ff7f0e;">Cluster 3</span>: Low-volume Victim – Mixed</strong> <br>
Subreddits that almost exclusively receive toxicity without responding. <br>
<u>Interpretation:</u> vulnerable communities subject to one-sided harassment, with little or no toxic output.
</li>
</ul>
<div class="box-remark box-collapsible">
<div class="box-header">
<div>
<strong>Remark - </strong>How do we assign labels (pattern, role, volume)?
</div>
<button class="box-toggle" aria-label="Toggle details"></button>
</div>
<div class="box-content">
<p><strong>Pattern assignment (chronic / bursty / occasional)</strong>. Thresholds are data-driven (tertiles).
We take the most frequent <code>temporal_pattern</code> inside the cluster (and keep its percentage as context).</p>
<ul class = "ulpack">
<li>Chronic: high activity + low irregularity.</li>
<li>Bursty: high short-window intensity + high irregularity.</li>
<li>Occasional: low activity + large typical gaps.</li>
<li>Mixed: A mix of the above categories</li>
</ul>
<p><strong>Volume assignment (low / moderate / high)</strong>: we compute tertiles on all subreddits, then assign:</p>
<ul class = "ulpack">
<li><strong>High-volume</strong> if cluster median <code>total_tox_count_raw</code> > Q0.67</li>
<li><strong>Moderate-volume</strong> if Q0.33 < median <= Q0.67.</li>
<li><strong>Low-volume</strong> otherwise</li>
</ul>
<p><strong>• Role label (aggressor / victim / balanced / leaning)</strong>: we compare outgoing vs incoming toxicity using the
relative imbalance computed on cluster medians <code>diff_ratio = abs(src - tgt) / (src + tgt)</code>, then assign:</p>
<ul class = "ulpack">
<li><strong>Balanced (Hub)</strong> if <code>diff_ratio</code> < 0.20</li>
<li><strong>Aggressor or Victim</strong> if <code>diff_ratio</code> >= 0.50 (depending on wether <code>src > tgt</code> or <code>tgt > src</code> </li>
<li><strong>Aggressor-leaning or Victim-leaning</strong> if 0.20 <= <code>diff_ratio</code> < 0.50.</li>
<li><strong>Inactive</strong> if <code>src + tgt</code> = 0.</li>
</ul>
<p><strong>• Mega-subreddits</strong>: We print how they distribute across clusters (transparency, not a separate type).</p>
</div>
</div>
<p>
Together, these propagation styles form a concise map of how toxicity spreads, escalates, and
concentrates across the Reddit archipelago. The result of this clustering can be visualized in the following map. This map <strong>is
not the geographic representation of Reddit from the 300-dimensional embeddings</strong>.
Instead, it is a virtual, statistical map in which each point represents a subreddit
positioned according to the similarities in its toxic behavior. Hover on the map to explore how islands reorganized!
</p>
<iframe src="assets/interactives/reddit_maps/subreddit_clustering_interactive.html" width="100%" height=720px style="border: none;"></iframe>
<h3>Marking variants on the Reddit map</h3>
<p>
We now want to see how well the identified variants align with the geographic structure of the Reddit archipelago: we color the islands
in our original embeddings-derived map according to their toxicity propagation style. Subreddits that are not toxic and thus
unlabeled are assigned to <strong><span style = "color:#bbbbbb;">Cluster -1</span></strong>.
</p>
<iframe src="assets/interactives/reddit_maps/reddit_map_cluster.html" width="100%" height = "630px" style="border: none;"></iframe>
<p>
The result provides a striking view. The immediate observation is that subreddits that are geographically distant in the embedding
space can share similar toxic behavior, while neighboring communities may follow very different dynamics. But at the same time, some regions
clearly display a dominant variant of toxicity behavior.
</p>
<div class="box-success">
<strong>Great!</strong> This cross-comparison gives us a deeper understanding of how the virus propagates both locally and across
the thematic landscape of Reddit.
</div>
<p>
With the Reddit archipelago fully mapped and the diverse variants of virus propagation clearly identified, the terrain is no longer unknown and our instruments are calibrated.
But understanding patterns from afar is only the first step: <strong>true insight comes from going into the field</strong>.
</p>
<p>
Equipped with our clusters and toxicity metrics, we prepared to <strong>conduct focused case studies</strong>. These are targeted, in-depth investigations of smaller regions
within the Reddit landscape, restricted to specific time windows and selected subreddits.
We designed 3 case studies cases. By zooming in on these representative clusters, we aim to capture the dynamics of toxicity in action: which communities amplify it, which absorb it, and how bursts ripple across connected islands.
And so, the team embarks on this next stage of the mission: collecting fine-grained evidence and understanding the mechanisms behind toxicity propagation in the Reddit archipelago...
</p>
</section>
<!-- #################################################################################################################################### -->
<!-- Case studies -->
<!-- #################################################################################################################################### -->
<section id="studycases" class="section-block">
<h1>Case studies</h1>
<p>
We aim to deepen our comprehension of the toxic events through particular examples. By performing an analysis on a <strong>specific time
period</strong> and for a <strong>carefully chosen set of subreddits</strong>, we expect to highlight previously-discussed behaviors, and discover additional
ones.
</p>
<p>
First, we select an interesting group of subreddits.
</p>
<ul>
<li>A typical subreddit part of a clustered behavior (high-volume balanced (hub) - bursty, low-volume victim - mixted, ...)
is chosen and its associated habitual neighbors are identified. To this set of neighbors, we add all subreddits interacting
with the initially chosen subreddit. Note that the union is kept, since some of its neighbors may interact with it.
This selected subreddit is called the <strong>protagonist</strong></li>
<img src="{{ '/assets/img/group_selection_scheme.png' | relative_url }}" alt="qcheme" class="center large">
</ul>
<p>
Second, we take a reduced time window.
</p>
<ul>
<li>From daily interactions over the whole time period (January 2014 to April 2017), we assess peaks and compare them with
daily negative interactions over the same period. A period of interest is qualitatively picked.</li>
</ul>
<p>
Third, we assess if the event is toxic or not.
</p>
<ul>
<li>The event is defined as the time series in the selected window of the total negative interactions of all subreddits
within the chosen group. Its nature is assessed from the daily activity of the group, and most active subreddits
(overall and toxic active) are highlighted. From the trend of negative interactions, we decide if this event is a potential
toxic one.</li>
</ul>
<p>
Finally, we analyze the life cycle of the event.
</p>
<ul>