diff --git a/notebooks/r6_analysis.ipynb b/notebooks/r6_analysis.ipynb
index 748d37a..1cb18df 100644
--- a/notebooks/r6_analysis.ipynb
+++ b/notebooks/r6_analysis.ipynb
@@ -12,7 +12,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"id": "setup",
"metadata": {},
"outputs": [],
@@ -54,10 +54,261 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"id": "task1-data-prep",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " GSG9-JAGER | \n",
+ " SWAT-ASH | \n",
+ " SAT-HIBANA | \n",
+ " GSG9-BANDIT | \n",
+ " GIGN-TWITCH | \n",
+ " SWAT-THERMITE | \n",
+ " SPETSNAZ-FUZE | \n",
+ " BOPE-CAVEIRA | \n",
+ " G.E.O.-JACKAL | \n",
+ " NAVYSEAL-VALKYRIE | \n",
+ " ... | \n",
+ " GSG9-IQ | \n",
+ " GIGN-MONTAGNE | \n",
+ " SAT-ECHO | \n",
+ " GSG9-BLITZ | \n",
+ " GSG9-RESERVE | \n",
+ " SPETSNAZ-TACHANKA | \n",
+ " SAS-RESERVE | \n",
+ " SWAT-RESERVE | \n",
+ " GIGN-RESERVE | \n",
+ " SPETSNAZ-RESERVE | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | total_kills | \n",
+ " 5522 | \n",
+ " 5446 | \n",
+ " 3825 | \n",
+ " 3802 | \n",
+ " 3577 | \n",
+ " 3441 | \n",
+ " 3336 | \n",
+ " 3293 | \n",
+ " 3135 | \n",
+ " 2926 | \n",
+ " ... | \n",
+ " 1027 | \n",
+ " 777 | \n",
+ " 775 | \n",
+ " 425 | \n",
+ " 386 | \n",
+ " 202 | \n",
+ " 183 | \n",
+ " 112 | \n",
+ " 89 | \n",
+ " 84 | \n",
+ "
\n",
+ " \n",
+ " | total_rounds | \n",
+ " 6999 | \n",
+ " 6519 | \n",
+ " 4960 | \n",
+ " 5257 | \n",
+ " 4894 | \n",
+ " 5001 | \n",
+ " 4222 | \n",
+ " 4586 | \n",
+ " 4091 | \n",
+ " 4319 | \n",
+ " ... | \n",
+ " 1570 | \n",
+ " 1880 | \n",
+ " 1207 | \n",
+ " 782 | \n",
+ " 618 | \n",
+ " 436 | \n",
+ " 375 | \n",
+ " 206 | \n",
+ " 173 | \n",
+ " 145 | \n",
+ "
\n",
+ " \n",
+ " | won_rounds | \n",
+ " 3535 | \n",
+ " 3370 | \n",
+ " 2586 | \n",
+ " 2684 | \n",
+ " 2553 | \n",
+ " 2603 | \n",
+ " 2164 | \n",
+ " 2321 | \n",
+ " 2140 | \n",
+ " 2177 | \n",
+ " ... | \n",
+ " 785 | \n",
+ " 953 | \n",
+ " 634 | \n",
+ " 376 | \n",
+ " 317 | \n",
+ " 196 | \n",
+ " 159 | \n",
+ " 97 | \n",
+ " 78 | \n",
+ " 65 | \n",
+ "
\n",
+ " \n",
+ " | dead_rounds | \n",
+ " 5127 | \n",
+ " 4555 | \n",
+ " 3294 | \n",
+ " 3833 | \n",
+ " 3180 | \n",
+ " 3394 | \n",
+ " 2929 | \n",
+ " 3362 | \n",
+ " 2850 | \n",
+ " 3150 | \n",
+ " ... | \n",
+ " 1128 | \n",
+ " 1284 | \n",
+ " 775 | \n",
+ " 585 | \n",
+ " 468 | \n",
+ " 329 | \n",
+ " 297 | \n",
+ " 151 | \n",
+ " 122 | \n",
+ " 107 | \n",
+ "
\n",
+ " \n",
+ " | survival_rate | \n",
+ " 0.267466780969 | \n",
+ " 0.301273201411 | \n",
+ " 0.335887096774 | \n",
+ " 0.270876926003 | \n",
+ " 0.350224765018 | \n",
+ " 0.321335732853 | \n",
+ " 0.306252960682 | \n",
+ " 0.266899258613 | \n",
+ " 0.303348814471 | \n",
+ " 0.270664505673 | \n",
+ " ... | \n",
+ " 0.28152866242 | \n",
+ " 0.317021276596 | \n",
+ " 0.357912178956 | \n",
+ " 0.251918158568 | \n",
+ " 0.242718446602 | \n",
+ " 0.245412844037 | \n",
+ " 0.208 | \n",
+ " 0.266990291262 | \n",
+ " 0.294797687861 | \n",
+ " 0.262068965517 | \n",
+ "
\n",
+ " \n",
+ " | win_rate | \n",
+ " 0.505072153165 | \n",
+ " 0.516950452523 | \n",
+ " 0.521370967742 | \n",
+ " 0.510557352102 | \n",
+ " 0.521659174499 | \n",
+ " 0.52049590082 | \n",
+ " 0.512553292279 | \n",
+ " 0.506105538596 | \n",
+ " 0.523099486678 | \n",
+ " 0.504051863857 | \n",
+ " ... | \n",
+ " 0.5 | \n",
+ " 0.506914893617 | \n",
+ " 0.525269262635 | \n",
+ " 0.480818414322 | \n",
+ " 0.512944983819 | \n",
+ " 0.449541284404 | \n",
+ " 0.424 | \n",
+ " 0.470873786408 | \n",
+ " 0.450867052023 | \n",
+ " 0.448275862069 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
6 rows × 35 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " GSG9-JAGER SWAT-ASH SAT-HIBANA GSG9-BANDIT \\\n",
+ "total_kills 5522 5446 3825 3802 \n",
+ "total_rounds 6999 6519 4960 5257 \n",
+ "won_rounds 3535 3370 2586 2684 \n",
+ "dead_rounds 5127 4555 3294 3833 \n",
+ "survival_rate 0.267466780969 0.301273201411 0.335887096774 0.270876926003 \n",
+ "win_rate 0.505072153165 0.516950452523 0.521370967742 0.510557352102 \n",
+ "\n",
+ " GIGN-TWITCH SWAT-THERMITE SPETSNAZ-FUZE BOPE-CAVEIRA \\\n",
+ "total_kills 3577 3441 3336 3293 \n",
+ "total_rounds 4894 5001 4222 4586 \n",
+ "won_rounds 2553 2603 2164 2321 \n",
+ "dead_rounds 3180 3394 2929 3362 \n",
+ "survival_rate 0.350224765018 0.321335732853 0.306252960682 0.266899258613 \n",
+ "win_rate 0.521659174499 0.52049590082 0.512553292279 0.506105538596 \n",
+ "\n",
+ " G.E.O.-JACKAL NAVYSEAL-VALKYRIE ... GSG9-IQ \\\n",
+ "total_kills 3135 2926 ... 1027 \n",
+ "total_rounds 4091 4319 ... 1570 \n",
+ "won_rounds 2140 2177 ... 785 \n",
+ "dead_rounds 2850 3150 ... 1128 \n",
+ "survival_rate 0.303348814471 0.270664505673 ... 0.28152866242 \n",
+ "win_rate 0.523099486678 0.504051863857 ... 0.5 \n",
+ "\n",
+ " GIGN-MONTAGNE SAT-ECHO GSG9-BLITZ GSG9-RESERVE \\\n",
+ "total_kills 777 775 425 386 \n",
+ "total_rounds 1880 1207 782 618 \n",
+ "won_rounds 953 634 376 317 \n",
+ "dead_rounds 1284 775 585 468 \n",
+ "survival_rate 0.317021276596 0.357912178956 0.251918158568 0.242718446602 \n",
+ "win_rate 0.506914893617 0.525269262635 0.480818414322 0.512944983819 \n",
+ "\n",
+ " SPETSNAZ-TACHANKA SAS-RESERVE SWAT-RESERVE GIGN-RESERVE \\\n",
+ "total_kills 202 183 112 89 \n",
+ "total_rounds 436 375 206 173 \n",
+ "won_rounds 196 159 97 78 \n",
+ "dead_rounds 329 297 151 122 \n",
+ "survival_rate 0.245412844037 0.208 0.266990291262 0.294797687861 \n",
+ "win_rate 0.449541284404 0.424 0.470873786408 0.450867052023 \n",
+ "\n",
+ " SPETSNAZ-RESERVE \n",
+ "total_kills 84 \n",
+ "total_rounds 145 \n",
+ "won_rounds 65 \n",
+ "dead_rounds 107 \n",
+ "survival_rate 0.262068965517 \n",
+ "win_rate 0.448275862069 \n",
+ "\n",
+ "[6 rows x 35 columns]"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
" \"*\"\n",
@@ -85,33 +336,6 @@
"operator_data.drop(labels=\"operator\")"
]
},
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "task1-kills",
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"\\n=== TOP 10 OPERATORS BY KILLS ===\")\n",
- "top_kills = df_operators.nlargest(10, \"kills\")[[\"operator\", \"kills\"]]\n",
- "for _, row in top_kills.iterrows():\n",
- " print(f\"{row['operator']}: {row['kills']} kills\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "task1-winrates",
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"\\n=== TOP 10 OPERATORS BY WIN RATE (min 100 rounds) ===\")\n",
- "filtered = df_operators[df_operators[\"rounds\"] >= 100]\n",
- "top_wr = filtered.nlargest(10, \"win_rate\")[[\"operator\", \"win_rate\", \"rounds\"]]\n",
- "for _, row in top_wr.iterrows():\n",
- " print(f\"{row['operator']}: {row['win_rate']}% win rate ({row['rounds']} rounds)\")"
- ]
- },
{
"cell_type": "markdown",
"id": "task2-header",
@@ -124,57 +348,205 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"id": "task2-weapons",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " 416-C CARBINE | \n",
+ " L85A2 | \n",
+ " MP7 | \n",
+ " R4-C | \n",
+ " 556XI | \n",
+ " TYPE-89 | \n",
+ " UMP45 | \n",
+ " MP5 | \n",
+ " F2 | \n",
+ " MPX | \n",
+ " ... | \n",
+ " SPAS-12 | \n",
+ " SASG-12 | \n",
+ " HK417 | \n",
+ " SUPER 90 | \n",
+ " SG-CQB | \n",
+ " SuperNova | \n",
+ " SR-25 | \n",
+ " G8A1 | \n",
+ " M249 | \n",
+ " ITA12L | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | primaryweapon | \n",
+ " 416-C CARBINE | \n",
+ " L85A2 | \n",
+ " MP7 | \n",
+ " R4-C | \n",
+ " 556XI | \n",
+ " TYPE-89 | \n",
+ " UMP45 | \n",
+ " MP5 | \n",
+ " F2 | \n",
+ " MPX | \n",
+ " ... | \n",
+ " SPAS-12 | \n",
+ " SASG-12 | \n",
+ " HK417 | \n",
+ " SUPER 90 | \n",
+ " SG-CQB | \n",
+ " SuperNova | \n",
+ " SR-25 | \n",
+ " G8A1 | \n",
+ " M249 | \n",
+ " ITA12L | \n",
+ "
\n",
+ " \n",
+ " | total_kills | \n",
+ " 5541 | \n",
+ " 3767 | \n",
+ " 3790 | \n",
+ " 4209 | \n",
+ " 3418 | \n",
+ " 3774 | \n",
+ " 3053 | \n",
+ " 2937 | \n",
+ " 3384 | \n",
+ " 2564 | \n",
+ " ... | \n",
+ " 362 | \n",
+ " 254 | \n",
+ " 210 | \n",
+ " 236 | \n",
+ " 187 | \n",
+ " 172 | \n",
+ " 173 | \n",
+ " 117 | \n",
+ " 94 | \n",
+ " 68 | \n",
+ "
\n",
+ " \n",
+ " | times_used | \n",
+ " 7005 | \n",
+ " 5371 | \n",
+ " 5216 | \n",
+ " 4993 | \n",
+ " 4964 | \n",
+ " 4884 | \n",
+ " 4726 | \n",
+ " 4693 | \n",
+ " 4527 | \n",
+ " 3794 | \n",
+ " ... | \n",
+ " 525 | \n",
+ " 468 | \n",
+ " 404 | \n",
+ " 358 | \n",
+ " 346 | \n",
+ " 296 | \n",
+ " 239 | \n",
+ " 198 | \n",
+ " 160 | \n",
+ " 141 | \n",
+ "
\n",
+ " \n",
+ " | wins | \n",
+ " 3535 | \n",
+ " 2833 | \n",
+ " 2668 | \n",
+ " 2572 | \n",
+ " 2582 | \n",
+ " 2554 | \n",
+ " 2348 | \n",
+ " 2414 | \n",
+ " 2379 | \n",
+ " 1934 | \n",
+ " ... | \n",
+ " 243 | \n",
+ " 215 | \n",
+ " 194 | \n",
+ " 192 | \n",
+ " 156 | \n",
+ " 134 | \n",
+ " 118 | \n",
+ " 99 | \n",
+ " 87 | \n",
+ " 68 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
4 rows × 46 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " 416-C CARBINE L85A2 MP7 R4-C 556XI TYPE-89 UMP45 MP5 \\\n",
+ "primaryweapon 416-C CARBINE L85A2 MP7 R4-C 556XI TYPE-89 UMP45 MP5 \n",
+ "total_kills 5541 3767 3790 4209 3418 3774 3053 2937 \n",
+ "times_used 7005 5371 5216 4993 4964 4884 4726 4693 \n",
+ "wins 3535 2833 2668 2572 2582 2554 2348 2414 \n",
+ "\n",
+ " F2 MPX ... SPAS-12 SASG-12 HK417 SUPER 90 SG-CQB \\\n",
+ "primaryweapon F2 MPX ... SPAS-12 SASG-12 HK417 SUPER 90 SG-CQB \n",
+ "total_kills 3384 2564 ... 362 254 210 236 187 \n",
+ "times_used 4527 3794 ... 525 468 404 358 346 \n",
+ "wins 2379 1934 ... 243 215 194 192 156 \n",
+ "\n",
+ " SuperNova SR-25 G8A1 M249 ITA12L \n",
+ "primaryweapon SuperNova SR-25 G8A1 M249 ITA12L \n",
+ "total_kills 172 173 117 94 68 \n",
+ "times_used 296 239 198 160 141 \n",
+ "wins 134 118 99 87 68 \n",
+ "\n",
+ "[4 rows x 46 columns]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "def get_weapon_stats(weapon_field=\"primaryweapon\"):\n",
- " \"\"\"Get weapon statistics using Redis aggregation.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": f\"${weapon_field}\",\n",
- " \"total_kills\": {\"$sum\": \"$nbkills\"},\n",
- " \"times_used\": {\"$sum\": 1},\n",
- " \"wins\": {\"$sum\": \"$haswon\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"times_used\": -1}},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"weapon\": r[\"_id\"],\n",
- " \"kills\": r[\"total_kills\"],\n",
- " \"usage_count\": r[\"times_used\"],\n",
- " \"wins\": r[\"wins\"],\n",
- " \"avg_kills_per_use\": round(r[\"total_kills\"] / r[\"times_used\"], 3) if r[\"times_used\"] > 0 else 0,\n",
- " \"win_rate\": round(r[\"wins\"] / r[\"times_used\"] * 100, 2) if r[\"times_used\"] > 0 else 0,\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
+ "query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
+ " \"*\"\n",
+ " ).group_by(\n",
+ " \"@primaryweapon\",\n",
+ " reducers.sum(\"@nbkills\").alias(\"total_kills\"),\n",
+ " reducers.count().alias(\"times_used\"),\n",
+ " reducers.sum(\"@haswon\").alias(\"wins\"),\n",
+ " ).sort_by(Desc(\"@times_used\")) # pyright: ignore[reportArgumentType]\n",
+ " .limit(0, 35565).cursor(10, 1)\n",
+ ")\n",
"\n",
- "print(\"Computing primary weapon statistics...\")\n",
- "primary_weapon_stats = get_weapon_stats(\"primaryweapon\")\n",
+ "response = client.ft().aggregate(query)\n",
+ "rows = response[0][\"results\"]\n",
+ "cursor = response[1]\n",
+ "while cursor > 0:\n",
+ " response= client.ft().aggregate(Cursor(cursor))\n",
+ " rows.extend(response[0][\"results\"])\n",
+ " cursor = response[1]\n",
+ "\n",
+ "primary_weapon_stats = pd.DataFrame.from_dict({attrs[\"extra_attributes\"][\"primaryweapon\"]: attrs[\"extra_attributes\"] for attrs in rows})\n",
"df_primary = pd.DataFrame(primary_weapon_stats)\n",
- "print(f\"\\nTotal unique primary weapons: {len(df_primary)}\")\n",
- "display(df_primary.head(20))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "task2-secondary",
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"\\n=== TOP 10 SECONDARY WEAPONS BY USAGE ===\")\n",
- "secondary_stats = get_weapon_stats(\"secondaryweapon\")\n",
- "df_secondary = pd.DataFrame(secondary_stats)\n",
- "display(df_secondary.head(10))"
+ "display(df_primary)"
]
},
{
@@ -189,83 +561,287 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"id": "task3-maps",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " SKYSCRAPER | \n",
+ " OREGON | \n",
+ " KAFE DOSTOYEVSKY | \n",
+ " BORDER | \n",
+ " HEREFORD BASE | \n",
+ " CHALET | \n",
+ " YACHT | \n",
+ " HOUSE | \n",
+ " PLANE | \n",
+ " KANAL | \n",
+ " FAVELAS | \n",
+ " COASTLINE | \n",
+ " CONSULATE | \n",
+ " BANK | \n",
+ " CLUB HOUSE | \n",
+ " BARTLETT U. | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | mapname | \n",
+ " SKYSCRAPER | \n",
+ " OREGON | \n",
+ " KAFE DOSTOYEVSKY | \n",
+ " BORDER | \n",
+ " HEREFORD BASE | \n",
+ " CHALET | \n",
+ " YACHT | \n",
+ " HOUSE | \n",
+ " PLANE | \n",
+ " KANAL | \n",
+ " FAVELAS | \n",
+ " COASTLINE | \n",
+ " CONSULATE | \n",
+ " BANK | \n",
+ " CLUB HOUSE | \n",
+ " BARTLETT U. | \n",
+ "
\n",
+ " \n",
+ " | total_rounds | \n",
+ " 7459 | \n",
+ " 7367 | \n",
+ " 7317 | \n",
+ " 6970 | \n",
+ " 6797 | \n",
+ " 6772 | \n",
+ " 6576 | \n",
+ " 6572 | \n",
+ " 6461 | \n",
+ " 6149 | \n",
+ " 6057 | \n",
+ " 5812 | \n",
+ " 5792 | \n",
+ " 5767 | \n",
+ " 5595 | \n",
+ " 5235 | \n",
+ "
\n",
+ " \n",
+ " | won_rounds | \n",
+ " 3789 | \n",
+ " 3761 | \n",
+ " 3732 | \n",
+ " 3547 | \n",
+ " 3452 | \n",
+ " 3449 | \n",
+ " 3347 | \n",
+ " 3333 | \n",
+ " 3300 | \n",
+ " 3114 | \n",
+ " 3089 | \n",
+ " 2941 | \n",
+ " 2981 | \n",
+ " 2947 | \n",
+ " 2837 | \n",
+ " 2656 | \n",
+ "
\n",
+ " \n",
+ " | round_duration | \n",
+ " 180.164097064 | \n",
+ " 187.90946111 | \n",
+ " 189.810714774 | \n",
+ " 179.218077475 | \n",
+ " 183.516698543 | \n",
+ " 180.794595393 | \n",
+ " 180.983880779 | \n",
+ " 179.114120511 | \n",
+ " 186.111437858 | \n",
+ " 188.070255326 | \n",
+ " 170.701337296 | \n",
+ " 183.439779766 | \n",
+ " 187.277106354 | \n",
+ " 192.193341425 | \n",
+ " 187.16997319 | \n",
+ " 191.232473734 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " SKYSCRAPER OREGON KAFE DOSTOYEVSKY BORDER \\\n",
+ "mapname SKYSCRAPER OREGON KAFE DOSTOYEVSKY BORDER \n",
+ "total_rounds 7459 7367 7317 6970 \n",
+ "won_rounds 3789 3761 3732 3547 \n",
+ "round_duration 180.164097064 187.90946111 189.810714774 179.218077475 \n",
+ "\n",
+ " HEREFORD BASE CHALET YACHT HOUSE \\\n",
+ "mapname HEREFORD BASE CHALET YACHT HOUSE \n",
+ "total_rounds 6797 6772 6576 6572 \n",
+ "won_rounds 3452 3449 3347 3333 \n",
+ "round_duration 183.516698543 180.794595393 180.983880779 179.114120511 \n",
+ "\n",
+ " PLANE KANAL FAVELAS COASTLINE \\\n",
+ "mapname PLANE KANAL FAVELAS COASTLINE \n",
+ "total_rounds 6461 6149 6057 5812 \n",
+ "won_rounds 3300 3114 3089 2941 \n",
+ "round_duration 186.111437858 188.070255326 170.701337296 183.439779766 \n",
+ "\n",
+ " CONSULATE BANK CLUB HOUSE BARTLETT U. \n",
+ "mapname CONSULATE BANK CLUB HOUSE BARTLETT U. \n",
+ "total_rounds 5792 5767 5595 5235 \n",
+ "won_rounds 2981 2947 2837 2656 \n",
+ "round_duration 187.277106354 192.193341425 187.16997319 191.232473734 "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "def get_map_stats():\n",
- " \"\"\"Get map statistics using Redis aggregation.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": \"$mapname\",\n",
- " \"total_rounds\": {\"$sum\": 1},\n",
- " \"avg_duration\": {\"$avg\": \"$roundduration\"},\n",
- " \"avg_clearance\": {\"$avg\": \"$clearancelevel\"},\n",
- " \"total_kills\": {\"$sum\": \"$nbkills\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"total_rounds\": -1}},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"map\": r[\"_id\"],\n",
- " \"rounds_played\": r[\"total_rounds\"],\n",
- " \"avg_duration_sec\": round(r[\"avg_duration\"], 2),\n",
- " \"avg_clearance_level\": round(r[\"avg_clearance\"], 2),\n",
- " \"total_kills\": r[\"total_kills\"],\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
+ "query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
+ " \"*\"\n",
+ " ).group_by(\n",
+ " \"@mapname\",\n",
+ " reducers.count().alias(\"total_rounds\"),\n",
+ " reducers.sum(\"@haswon\").alias(\"won_rounds\"),\n",
+ " reducers.avg(\"@roundduration\").alias(\"round_duration\")\n",
+ " ).sort_by(Desc(\"@total_rounds\")) # pyright: ignore[reportArgumentType]\n",
+ " .limit(0, 35565).cursor(10, 1)\n",
+ ")\n",
"\n",
- "print(\"Computing map statistics...\")\n",
- "map_stats = get_map_stats()\n",
- "df_maps = pd.DataFrame(map_stats)\n",
- "display(df_maps.head(15))"
+ "response = client.ft().aggregate(query)\n",
+ "rows = response[0][\"results\"]\n",
+ "cursor = response[1]\n",
+ "while cursor > 0:\n",
+ " response= client.ft().aggregate(Cursor(cursor))\n",
+ " rows.extend(response[0][\"results\"])\n",
+ " cursor = response[1]\n",
+ "\n",
+ "df_maps = pd.DataFrame.from_dict({attrs[\"extra_attributes\"][\"mapname\"]: attrs[\"extra_attributes\"] for attrs in rows})\n",
+ "display(df_maps)"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 10,
"id": "task3-gamemode",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " PvP SECURE AREA | \n",
+ " PvP BOMB | \n",
+ " PvP HOSTAGE | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | gamemode | \n",
+ " PvP SECURE AREA | \n",
+ " PvP BOMB | \n",
+ " PvP HOSTAGE | \n",
+ "
\n",
+ " \n",
+ " | total_rounds | \n",
+ " 66034 | \n",
+ " 25872 | \n",
+ " 10792 | \n",
+ "
\n",
+ " \n",
+ " | won_rounds | \n",
+ " 33582 | \n",
+ " 13183 | \n",
+ " 5510 | \n",
+ "
\n",
+ " \n",
+ " | total_kills | \n",
+ " 46375 | \n",
+ " 18364 | \n",
+ " 7275 | \n",
+ "
\n",
+ " \n",
+ " | round_duration | \n",
+ " 183.826771057 | \n",
+ " 185.54363791 | \n",
+ " 182.205707932 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " PvP SECURE AREA PvP BOMB PvP HOSTAGE\n",
+ "gamemode PvP SECURE AREA PvP BOMB PvP HOSTAGE\n",
+ "total_rounds 66034 25872 10792\n",
+ "won_rounds 33582 13183 5510\n",
+ "total_kills 46375 18364 7275\n",
+ "round_duration 183.826771057 185.54363791 182.205707932"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "def get_gamemode_stats():\n",
- " \"\"\"Get game mode statistics.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": \"$gamemode\",\n",
- " \"total_rounds\": {\"$sum\": 1},\n",
- " \"avg_duration\": {\"$avg\": \"$roundduration\"},\n",
- " \"total_kills\": {\"$sum\": \"$nbkills\"},\n",
- " \"unique_matches\": {\"$addToSet\": \"$matchid\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"total_rounds\": -1}},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"gamemode\": r[\"_id\"],\n",
- " \"rounds_played\": r[\"total_rounds\"],\n",
- " \"avg_duration_sec\": round(r[\"avg_duration\"], 2),\n",
- " \"total_kills\": r[\"total_kills\"],\n",
- " \"unique_matches\": len(r[\"unique_matches\"]),\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
+ "query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
+ " \"*\"\n",
+ " ).group_by(\n",
+ " \"@gamemode\",\n",
+ " reducers.count().alias(\"total_rounds\"),\n",
+ " reducers.sum(\"@haswon\").alias(\"won_rounds\"),\n",
+ " reducers.avg(\"@roundduration\").alias(\"round_duration\"),\n",
+ " reducers.sum(\"@nbkills\").alias(\"total_kills\")\n",
+ " ).sort_by(Desc(\"@total_rounds\")) # pyright: ignore[reportArgumentType]\n",
+ " .limit(0, 35565).cursor(10, 1)\n",
+ ")\n",
"\n",
- "print(\"=== GAME MODE BREAKDOWN ===\")\n",
- "gamemode_stats = get_gamemode_stats()\n",
- "df_gamemode = pd.DataFrame(gamemode_stats)\n",
+ "response = client.ft().aggregate(query)\n",
+ "rows = response[0][\"results\"]\n",
+ "cursor = response[1]\n",
+ "while cursor > 0:\n",
+ " response= client.ft().aggregate(Cursor(cursor))\n",
+ " rows.extend(response[0][\"results\"])\n",
+ " cursor = response[1]\n",
+ "\n",
+ "\n",
+ "df_gamemode = pd.DataFrame.from_dict({attrs[\"extra_attributes\"][\"gamemode\"]: attrs[\"extra_attributes\"] for attrs in rows})\n",
"display(df_gamemode)"
]
},
@@ -281,62 +857,153 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 17,
"id": "task4-ranks",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Gold | \n",
+ " Silver | \n",
+ " Copper | \n",
+ " Bronze | \n",
+ " Platinum | \n",
+ " Unranked | \n",
+ " Diamond | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | skillrank | \n",
+ " Gold | \n",
+ " Silver | \n",
+ " Copper | \n",
+ " Bronze | \n",
+ " Platinum | \n",
+ " Unranked | \n",
+ " Diamond | \n",
+ "
\n",
+ " \n",
+ " | total_rounds | \n",
+ " 35770 | \n",
+ " 20271 | \n",
+ " 13996 | \n",
+ " 13611 | \n",
+ " 11567 | \n",
+ " 6797 | \n",
+ " 686 | \n",
+ "
\n",
+ " \n",
+ " | won_rounds | \n",
+ " 18523 | \n",
+ " 10395 | \n",
+ " 6959 | \n",
+ " 6870 | \n",
+ " 5946 | \n",
+ " 3174 | \n",
+ " 408 | \n",
+ "
\n",
+ " \n",
+ " | total_kills | \n",
+ " 26270 | \n",
+ " 13804 | \n",
+ " 9014 | \n",
+ " 9062 | \n",
+ " 9362 | \n",
+ " 3856 | \n",
+ " 646 | \n",
+ "
\n",
+ " \n",
+ " | total_deaths | \n",
+ " 25039 | \n",
+ " 14297 | \n",
+ " 9912 | \n",
+ " 9656 | \n",
+ " 8078 | \n",
+ " 4987 | \n",
+ " 431 | \n",
+ "
\n",
+ " \n",
+ " | round_duration | \n",
+ " 187.55789768 | \n",
+ " 183.711558384 | \n",
+ " 173.63875393 | \n",
+ " 180.108809052 | \n",
+ " 190.132013487 | \n",
+ " 185.866411652 | \n",
+ " 187.030612245 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Gold Silver Copper Bronze \\\n",
+ "skillrank Gold Silver Copper Bronze \n",
+ "total_rounds 35770 20271 13996 13611 \n",
+ "won_rounds 18523 10395 6959 6870 \n",
+ "total_kills 26270 13804 9014 9062 \n",
+ "total_deaths 25039 14297 9912 9656 \n",
+ "round_duration 187.55789768 183.711558384 173.63875393 180.108809052 \n",
+ "\n",
+ " Platinum Unranked Diamond \n",
+ "skillrank Platinum Unranked Diamond \n",
+ "total_rounds 11567 6797 686 \n",
+ "won_rounds 5946 3174 408 \n",
+ "total_kills 9362 3856 646 \n",
+ "total_deaths 8078 4987 431 \n",
+ "round_duration 190.132013487 185.866411652 187.030612245 "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "def get_rank_stats():\n",
- " \"\"\"Get skill rank statistics.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": \"$skillrank\",\n",
- " \"total_rounds\": {\"$sum\": 1},\n",
- " \"total_kills\": {\"$sum\": \"$nbkills\"},\n",
- " \"wins\": {\"$sum\": \"$haswon\"},\n",
- " \"deaths\": {\"$sum\": \"$isdead\"},\n",
- " \"avg_kills\": {\"$avg\": \"$nbkills\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"total_kills\": -1}},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"rank\": r[\"_id\"],\n",
- " \"rounds\": r[\"total_rounds\"],\n",
- " \"kills\": r[\"total_kills\"],\n",
- " \"wins\": r[\"wins\"],\n",
- " \"deaths\": r[\"deaths\"],\n",
- " \"avg_kills\": round(r[\"avg_kills\"], 3),\n",
- " \"win_rate\": round(r[\"wins\"] / r[\"total_rounds\"] * 100, 2) if r[\"total_rounds\"] > 0 else 0,\n",
- " \"kda\": round(r[\"total_kills\"] / max(r[\"deaths\"], 1), 2),\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
+ "query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
+ " \"*\"\n",
+ " ).group_by(\n",
+ " \"@skillrank\",\n",
+ " reducers.count().alias(\"total_rounds\"),\n",
+ " reducers.sum(\"@haswon\").alias(\"won_rounds\"),\n",
+ " reducers.avg(\"@roundduration\").alias(\"round_duration\"),\n",
+ " reducers.sum(\"@nbkills\").alias(\"total_kills\"),\n",
+ " reducers.sum(\"@isdead\").alias(\"total_deaths\")\n",
+ " ).sort_by(Desc(\"@total_rounds\")) # pyright: ignore[reportArgumentType]\n",
+ " .limit(0, 35565).cursor(10, 1)\n",
+ ")\n",
"\n",
- "print(\"Computing skill rank statistics...\")\n",
- "rank_stats = get_rank_stats()\n",
- "df_ranks = pd.DataFrame(rank_stats)\n",
+ "response = client.ft().aggregate(query)\n",
+ "rows = response[0][\"results\"]\n",
+ "cursor = response[1]\n",
+ "while cursor > 0:\n",
+ " response= client.ft().aggregate(Cursor(cursor))\n",
+ " rows.extend(response[0][\"results\"])\n",
+ " cursor = response[1]\n",
+ "\n",
+ "df_ranks = pd.DataFrame.from_dict({attrs[\"extra_attributes\"][\"skillrank\"]: attrs[\"extra_attributes\"] for attrs in rows})\n",
"display(df_ranks)"
]
},
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "task4-visualization",
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"\\n=== KILLS PER ROUND BY RANK ===\")\n",
- "for _, row in df_ranks.iterrows():\n",
- " bar = \"█\" * int(row['avg_kills'] * 10)\n",
- " print(f\"{row['rank']:15} | {bar} {row['avg_kills']:.3f}\")"
- ]
- },
{
"cell_type": "markdown",
"id": "task5-header",
@@ -349,88 +1016,246 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "id": "task5-roles",
- "metadata": {},
- "outputs": [],
- "source": [
- "def get_role_stats():\n",
- " \"\"\"Get role statistics.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": \"$role\",\n",
- " \"total_rounds\": {\"$sum\": 1},\n",
- " \"total_kills\": {\"$sum\": \"$nbkills\"},\n",
- " \"wins\": {\"$sum\": \"$haswon\"},\n",
- " \"operators_used\": {\"$addToSet\": \"$operator\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"total_rounds\": -1}},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"role\": r[\"_id\"],\n",
- " \"rounds\": r[\"total_rounds\"],\n",
- " \"kills\": r[\"total_kills\"],\n",
- " \"wins\": r[\"wins\"],\n",
- " \"unique_operators\": len(r[\"operators_used\"]),\n",
- " \"win_rate\": round(r[\"wins\"] / r[\"total_rounds\"] * 100, 2) if r[\"total_rounds\"] > 0 else 0,\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
- "\n",
- "print(\"=== ROLE DISTRIBUTION ===\")\n",
- "role_stats = get_role_stats()\n",
- "df_roles = pd.DataFrame(role_stats)\n",
- "display(df_roles)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
+ "execution_count": 19,
"id": "task5-pivot",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " GSG9-JAGER | \n",
+ " SWAT-ASH | \n",
+ " GSG9-BANDIT | \n",
+ " SWAT-THERMITE | \n",
+ " SAT-HIBANA | \n",
+ " GIGN-TWITCH | \n",
+ " BOPE-CAVEIRA | \n",
+ " NAVYSEAL-VALKYRIE | \n",
+ " SPETSNAZ-FUZE | \n",
+ " G.E.O.-JACKAL | \n",
+ " ... | \n",
+ " GSG9-IQ | \n",
+ " BOPE-CAPITAO | \n",
+ " SAT-ECHO | \n",
+ " GSG9-BLITZ | \n",
+ " SPETSNAZ-TACHANKA | \n",
+ " GSG9-RESERVE | \n",
+ " SAS-RESERVE | \n",
+ " SWAT-RESERVE | \n",
+ " GIGN-RESERVE | \n",
+ " SPETSNAZ-RESERVE | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | role | \n",
+ " Defender | \n",
+ " Attacker | \n",
+ " Defender | \n",
+ " Attacker | \n",
+ " Attacker | \n",
+ " Attacker | \n",
+ " Defender | \n",
+ " Defender | \n",
+ " Attacker | \n",
+ " Attacker | \n",
+ " ... | \n",
+ " Attacker | \n",
+ " Attacker | \n",
+ " Defender | \n",
+ " Attacker | \n",
+ " Defender | \n",
+ " Attacker | \n",
+ " Attacker | \n",
+ " Defender | \n",
+ " Attacker | \n",
+ " Defender | \n",
+ "
\n",
+ " \n",
+ " | operator | \n",
+ " GSG9-JAGER | \n",
+ " SWAT-ASH | \n",
+ " GSG9-BANDIT | \n",
+ " SWAT-THERMITE | \n",
+ " SAT-HIBANA | \n",
+ " GIGN-TWITCH | \n",
+ " BOPE-CAVEIRA | \n",
+ " NAVYSEAL-VALKYRIE | \n",
+ " SPETSNAZ-FUZE | \n",
+ " G.E.O.-JACKAL | \n",
+ " ... | \n",
+ " GSG9-IQ | \n",
+ " BOPE-CAPITAO | \n",
+ " SAT-ECHO | \n",
+ " GSG9-BLITZ | \n",
+ " SPETSNAZ-TACHANKA | \n",
+ " GSG9-RESERVE | \n",
+ " SAS-RESERVE | \n",
+ " SWAT-RESERVE | \n",
+ " GIGN-RESERVE | \n",
+ " SPETSNAZ-RESERVE | \n",
+ "
\n",
+ " \n",
+ " | total_rounds | \n",
+ " 6999 | \n",
+ " 6519 | \n",
+ " 5257 | \n",
+ " 5001 | \n",
+ " 4960 | \n",
+ " 4894 | \n",
+ " 4586 | \n",
+ " 4319 | \n",
+ " 4222 | \n",
+ " 4091 | \n",
+ " ... | \n",
+ " 1570 | \n",
+ " 1554 | \n",
+ " 1207 | \n",
+ " 782 | \n",
+ " 436 | \n",
+ " 219 | \n",
+ " 165 | \n",
+ " 84 | \n",
+ " 77 | \n",
+ " 70 | \n",
+ "
\n",
+ " \n",
+ " | total_kills | \n",
+ " 5522 | \n",
+ " 5446 | \n",
+ " 3802 | \n",
+ " 3441 | \n",
+ " 3825 | \n",
+ " 3577 | \n",
+ " 3293 | \n",
+ " 2926 | \n",
+ " 3336 | \n",
+ " 3135 | \n",
+ " ... | \n",
+ " 1027 | \n",
+ " 1082 | \n",
+ " 775 | \n",
+ " 425 | \n",
+ " 202 | \n",
+ " 139 | \n",
+ " 91 | \n",
+ " 45 | \n",
+ " 42 | \n",
+ " 37 | \n",
+ "
\n",
+ " \n",
+ " | total_deaths | \n",
+ " 5127 | \n",
+ " 4555 | \n",
+ " 3833 | \n",
+ " 3394 | \n",
+ " 3294 | \n",
+ " 3180 | \n",
+ " 3362 | \n",
+ " 3150 | \n",
+ " 2929 | \n",
+ " 2850 | \n",
+ " ... | \n",
+ " 1128 | \n",
+ " 1105 | \n",
+ " 775 | \n",
+ " 585 | \n",
+ " 329 | \n",
+ " 162 | \n",
+ " 119 | \n",
+ " 66 | \n",
+ " 49 | \n",
+ " 57 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
5 rows × 35 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " GSG9-JAGER SWAT-ASH GSG9-BANDIT SWAT-THERMITE SAT-HIBANA \\\n",
+ "role Defender Attacker Defender Attacker Attacker \n",
+ "operator GSG9-JAGER SWAT-ASH GSG9-BANDIT SWAT-THERMITE SAT-HIBANA \n",
+ "total_rounds 6999 6519 5257 5001 4960 \n",
+ "total_kills 5522 5446 3802 3441 3825 \n",
+ "total_deaths 5127 4555 3833 3394 3294 \n",
+ "\n",
+ " GIGN-TWITCH BOPE-CAVEIRA NAVYSEAL-VALKYRIE SPETSNAZ-FUZE \\\n",
+ "role Attacker Defender Defender Attacker \n",
+ "operator GIGN-TWITCH BOPE-CAVEIRA NAVYSEAL-VALKYRIE SPETSNAZ-FUZE \n",
+ "total_rounds 4894 4586 4319 4222 \n",
+ "total_kills 3577 3293 2926 3336 \n",
+ "total_deaths 3180 3362 3150 2929 \n",
+ "\n",
+ " G.E.O.-JACKAL ... GSG9-IQ BOPE-CAPITAO SAT-ECHO \\\n",
+ "role Attacker ... Attacker Attacker Defender \n",
+ "operator G.E.O.-JACKAL ... GSG9-IQ BOPE-CAPITAO SAT-ECHO \n",
+ "total_rounds 4091 ... 1570 1554 1207 \n",
+ "total_kills 3135 ... 1027 1082 775 \n",
+ "total_deaths 2850 ... 1128 1105 775 \n",
+ "\n",
+ " GSG9-BLITZ SPETSNAZ-TACHANKA GSG9-RESERVE SAS-RESERVE \\\n",
+ "role Attacker Defender Attacker Attacker \n",
+ "operator GSG9-BLITZ SPETSNAZ-TACHANKA GSG9-RESERVE SAS-RESERVE \n",
+ "total_rounds 782 436 219 165 \n",
+ "total_kills 425 202 139 91 \n",
+ "total_deaths 585 329 162 119 \n",
+ "\n",
+ " SWAT-RESERVE GIGN-RESERVE SPETSNAZ-RESERVE \n",
+ "role Defender Attacker Defender \n",
+ "operator SWAT-RESERVE GIGN-RESERVE SPETSNAZ-RESERVE \n",
+ "total_rounds 84 77 70 \n",
+ "total_kills 45 42 37 \n",
+ "total_deaths 66 49 57 \n",
+ "\n",
+ "[5 rows x 35 columns]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "def get_role_operator_stats():\n",
- " \"\"\"Get operator usage by role.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": {\"role\": \"$role\", \"operator\": \"$operator\"},\n",
- " \"count\": {\"$sum\": 1},\n",
- " \"kills\": {\"$sum\": \"$nbkills\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"count\": -1}},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"role\": r[\"_id\"][\"role\"],\n",
- " \"operator\": r[\"_id\"][\"operator\"],\n",
- " \"usage\": r[\"count\"],\n",
- " \"kills\": r[\"kills\"],\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
+ "query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
+ " \"*\"\n",
+ " ).group_by(\n",
+ " [\"@role\", \"@operator\"],\n",
+ " reducers.count().alias(\"total_rounds\"),\n",
+ " reducers.sum(\"@nbkills\").alias(\"total_kills\"),\n",
+ " reducers.sum(\"@isdead\").alias(\"total_deaths\")\n",
+ " ).sort_by(Desc(\"@total_rounds\")) # pyright: ignore[reportArgumentType]\n",
+ " .limit(0, 35565).cursor(10, 1)\n",
+ ")\n",
"\n",
- "role_op_stats = get_role_operator_stats()\n",
- "df_role_op = pd.DataFrame(role_op_stats)\n",
+ "response = client.ft().aggregate(query)\n",
+ "rows = response[0][\"results\"]\n",
+ "cursor = response[1]\n",
+ "while cursor > 0:\n",
+ " response= client.ft().aggregate(Cursor(cursor))\n",
+ " rows.extend(response[0][\"results\"])\n",
+ " cursor = response[1]\n",
"\n",
- "print(\"\\n=== TOP 5 ATTACK OPERATORS ===\")\n",
- "att_op = df_role_op[df_role_op[\"role\"] == \"Attack\"].nlargest(5, \"usage\")\n",
- "display(att_op)\n",
+ "df_role_op = pd.DataFrame.from_dict({attrs[\"extra_attributes\"][\"operator\"]: attrs[\"extra_attributes\"] for attrs in rows})\n",
"\n",
- "print(\"\\n=== TOP 5 DEFENSE OPERATORS ===\")\n",
- "def_op = df_role_op[df_role_op[\"role\"] == \"Defense\"].nlargest(5, \"usage\")\n",
- "display(def_op)"
+ "display(df_role_op)"
]
},
{
@@ -445,47 +1270,200 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 23,
"id": "task6-matches",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " matchid | \n",
+ " mapname | \n",
+ " gamemode | \n",
+ " total_kills | \n",
+ " total_rounds | \n",
+ " max_duration | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 4293620789 | \n",
+ " 4293620789 | \n",
+ " CHALET | \n",
+ " PvP HOSTAGE | \n",
+ " 57 | \n",
+ " 9 | \n",
+ " 232 | \n",
+ "
\n",
+ " \n",
+ " | 1525148621 | \n",
+ " 1525148621 | \n",
+ " SKYSCRAPER | \n",
+ " PvP SECURE AREA | \n",
+ " 65 | \n",
+ " 9 | \n",
+ " 237 | \n",
+ "
\n",
+ " \n",
+ " | 1577581 | \n",
+ " 1577581 | \n",
+ " KAFE DOSTOYEVSKY | \n",
+ " PvP HOSTAGE | \n",
+ " 63 | \n",
+ " 9 | \n",
+ " 232 | \n",
+ "
\n",
+ " \n",
+ " | 4294842769 | \n",
+ " 4294842769 | \n",
+ " KAFE DOSTOYEVSKY | \n",
+ " PvP SECURE AREA | \n",
+ " 68 | \n",
+ " 9 | \n",
+ " 232 | \n",
+ "
\n",
+ " \n",
+ " | 2886541 | \n",
+ " 2886541 | \n",
+ " BARTLETT U. | \n",
+ " PvP SECURE AREA | \n",
+ " 52 | \n",
+ " 9 | \n",
+ " 257 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 4287504149 | \n",
+ " 4287504149 | \n",
+ " BARTLETT U. | \n",
+ " PvP BOMB | \n",
+ " 3 | \n",
+ " 1 | \n",
+ " 153 | \n",
+ "
\n",
+ " \n",
+ " | 4294525829 | \n",
+ " 4294525829 | \n",
+ " YACHT | \n",
+ " PvP BOMB | \n",
+ " 5 | \n",
+ " 1 | \n",
+ " 224 | \n",
+ "
\n",
+ " \n",
+ " | 1523059021 | \n",
+ " 1523059021 | \n",
+ " YACHT | \n",
+ " PvP SECURE AREA | \n",
+ " 8 | \n",
+ " 1 | \n",
+ " 133 | \n",
+ "
\n",
+ " \n",
+ " | 2766593549 | \n",
+ " 2766593549 | \n",
+ " BARTLETT U. | \n",
+ " PvP SECURE AREA | \n",
+ " 9 | \n",
+ " 1 | \n",
+ " 268 | \n",
+ "
\n",
+ " \n",
+ " | 3281101 | \n",
+ " 3281101 | \n",
+ " CLUB HOUSE | \n",
+ " PvP SECURE AREA | \n",
+ " 7 | \n",
+ " 1 | \n",
+ " 186 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
1900 rows × 6 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " matchid mapname gamemode total_kills \\\n",
+ "4293620789 4293620789 CHALET PvP HOSTAGE 57 \n",
+ "1525148621 1525148621 SKYSCRAPER PvP SECURE AREA 65 \n",
+ "1577581 1577581 KAFE DOSTOYEVSKY PvP HOSTAGE 63 \n",
+ "4294842769 4294842769 KAFE DOSTOYEVSKY PvP SECURE AREA 68 \n",
+ "2886541 2886541 BARTLETT U. PvP SECURE AREA 52 \n",
+ "... ... ... ... ... \n",
+ "4287504149 4287504149 BARTLETT U. PvP BOMB 3 \n",
+ "4294525829 4294525829 YACHT PvP BOMB 5 \n",
+ "1523059021 1523059021 YACHT PvP SECURE AREA 8 \n",
+ "2766593549 2766593549 BARTLETT U. PvP SECURE AREA 9 \n",
+ "3281101 3281101 CLUB HOUSE PvP SECURE AREA 7 \n",
+ "\n",
+ " total_rounds max_duration \n",
+ "4293620789 9 232 \n",
+ "1525148621 9 237 \n",
+ "1577581 9 232 \n",
+ "4294842769 9 232 \n",
+ "2886541 9 257 \n",
+ "... ... ... \n",
+ "4287504149 1 153 \n",
+ "4294525829 1 224 \n",
+ "1523059021 1 133 \n",
+ "2766593549 1 268 \n",
+ "3281101 1 186 \n",
+ "\n",
+ "[1900 rows x 6 columns]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "def get_match_stats():\n",
- " \"\"\"Get match-level statistics using Redis aggregation.\"\"\"\n",
- " aggr = client.ft().aggregate(\n",
- " \"*\",\n",
- " [\n",
- " {\n",
- " \"$group\": {\n",
- " \"_id\": \"$matchid\",\n",
- " \"total_players\": {\"$sum\": 1},\n",
- " \"total_kills\": {\"$sum\": \"$nbkills\"},\n",
- " \"total_rounds\": {\"$addToSet\": \"$roundnumber\"},\n",
- " \"map\": {\"$first\": \"$mapname\"},\n",
- " \"gamemode\": {\"$first\": \"$gamemode\"},\n",
- " \"max_duration\": {\"$max\": \"$roundduration\"},\n",
- " }\n",
- " },\n",
- " {\"$sort\": {\"total_kills\": -1}},\n",
- " {\"$limit\": 20},\n",
- " ]\n",
- " )\n",
- " return [\n",
- " {\n",
- " \"match_id\": r[\"_id\"],\n",
- " \"players\": r[\"total_players\"],\n",
- " \"total_kills\": r[\"total_kills\"],\n",
- " \"rounds_played\": len(r[\"total_rounds\"]),\n",
- " \"map\": r[\"map\"],\n",
- " \"gamemode\": r[\"gamemode\"],\n",
- " \"max_round_duration\": r[\"max_duration\"],\n",
- " }\n",
- " for r in aggr[\"results\"]\n",
- " ]\n",
+ "query = (AggregateRequest( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType, reportAssignmentType]\n",
+ " \"*\"\n",
+ " ).group_by(\n",
+ " [\"@matchid\", \"@mapname\", \"@gamemode\"],\n",
+ " reducers.sum(\"@nbkills\").alias(\"total_kills\"),\n",
+ " reducers.max(\"@roundnumber\").alias(\"total_rounds\"),\n",
+ " reducers.max(\"@roundduration\").alias(\"max_duration\")\n",
+ " ).sort_by(Desc(\"@total_rounds\")) # pyright: ignore[reportArgumentType]\n",
+ " .limit(0, 35565).cursor(10, 1)\n",
+ ")\n",
+ "\n",
+ "response = client.ft().aggregate(query)\n",
+ "rows = response[0][\"results\"]\n",
+ "cursor = response[1]\n",
+ "while cursor > 0:\n",
+ " response= client.ft().aggregate(Cursor(cursor))\n",
+ " rows.extend(response[0][\"results\"])\n",
+ " cursor = response[1]\n",
+ "\n",
+ "df_matches = pd.DataFrame.from_dict({attrs[\"extra_attributes\"][\"matchid\"]: attrs[\"extra_attributes\"] for attrs in rows}).transpose()\n",
"\n",
- "print(\"Computing match statistics (top 20 most violent matches)...\")\n",
- "match_stats = get_match_stats()\n",
- "df_matches = pd.DataFrame(match_stats)\n",
"display(df_matches)"
]
},
@@ -840,6 +1818,18 @@
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
+ },
+ "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.13.12"
}
},
"nbformat": 4,