← Back to list

What is the King of UFC Submissions?

After writing my piece on submissions at ADCC, I wanted to extend the analysis and a take a look at the UFC. I won’t go as in-depth as my…

Dominic Maniscalco · 2025-02-01 02:35 · 0 claps · 4.7 min read
#ufc #jiu-jitsu #python #data-visualization
Open on Medium ↗
Wiki topics: VIS · Visual & Graphic Design 🥊 · Combat Sports

What is the King of UFC Submissions?

After writing my piece on submissions at ADCC, I wanted to extend the analysis and a take a look at the UFC. I won’t go as in-depth as my previous article, but I still thought it would be fun to explore. To get right to the point once again, the Rear Naked Choke (RNC) reigns supreme, but let’s dig into the data!

First, I took the data from THIS kind user on Kaggle and imported it into a Google Colab notebook:

import kagglehub
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
# Download the dataset from kaggle
path = kagglehub.dataset_download("alexmagnus24/ufc-fight-statistics-july-2016-nov-2024")

# Use $ to keep python variable in the terminal command
!ls $path  # make sure there are files in the path as we expect
!cp -r $path/* /content/  # move to /content folder

# Read CSV file to a pandas df
data_csv = pd.read_csv('/content/UFC Fight Statistics (July 2016 - Nov 2024).csv')

Now that we have this UFC data imported, we need to make the submission names consistent as well as consolidate some of them under the same name (ex. choke, headlock, and necktie are all under “other choke”):

# Keep only rows that end in submission
subs_df = data_csv[data_csv['Fight Method'] == 'Submission']['Finish Details or Judges Scorecard']

# Change the subs to have consistent names
subs_series = subs_df.copy()
subs_series = subs_series.loc[~subs_series.str.contains('#VALUE!', case=False)]
subs_series.loc[subs_series.str.contains('rear naked', case=False)] = 'RNC'
subs_series.loc[subs_series.str.contains('triangle', case=False)] = 'Triangle'
subs_series.loc[subs_series.str.contains('armbar', case=False)] = 'Armbar'
subs_series.loc[subs_series.str.contains('guillotine', case=False)] = 'Guillotine'
subs_series.loc[subs_series.str.contains('kimura', case=False)] = 'Kimura'
subs_series.loc[subs_series.str.contains('anaconda', case=False)] = 'Anaconda'
subs_series.loc[subs_series.str.contains('heel hook', case=False)] = 'Heel Hook'
subs_series.loc[subs_series.str.contains("d'arce", case=False)] = "D'Arce"
subs_series.loc[subs_series.str.contains('choke|headlock|necktie', case=False)] = 'Other Choke'
subs_series.loc[subs_series.str.contains('kneebar|calf|z-lock|ankle', case=False)] = 'Other Leglock'
subs_series.loc[subs_series.str.contains('crank', case=False)] = 'Crank'

# Rename the series
subs_series = subs_series.rename('submission')

After doing this quick data cleaning, we can find the counts of each submission and then graph the results:

# Break out total counts of each submission for bar graph
sub_counts = subs_series.value_counts()
# Create the bar chart
plt.figure(figsize=(10, 6))
sns.barplot(x='submission', y='count', data=sub_counts.reset_index(),
            palette='viridis',
            hue='submission',  # use `x` variable for hue
            dodge=False,       # prevent stacking (for single-category bars)
            legend=False)      # disable legend since hue matches x

# Add labels and title
plt.title('Counts of UFC Submissions', fontsize=16)
plt.xlabel('Submission', fontsize=12)
plt.ylabel('Count', fontsize=12)
plt.xticks(rotation=90)  # rotate x-axis labels for better readability
plt.tight_layout()

# Show the plot
plt.show()

Figure 1

Figure 1

Figure 1 seems quite clear when we notice that the RNC is used to submit an opponent over 2x as often as the second-most frequent submission (the guillotine). As I mentioned previously, I won’t be breaking out the data as in-depth as other articles, but let’s break it out a tiny bit more by checking the top submissions by weight class.

The weight classes in this data set were labeled inconsistently, so we again have to do some cleaning to get the names organized:

# Make a copy so we don't get a warning
wt_class_df = data_csv.copy()

# Take out values that are not weight class names
wt_class_df = wt_class_df.loc[~wt_class_df['Bout'].str.contains('rear naked', case=False)]
wt_class_df = wt_class_df.loc[~wt_class_df['Bout'].str.contains('elbow', case=False)]
wt_class_df = wt_class_df.loc[~wt_class_df['Bout'].str.contains('-', case=False)]

# Change the name of women's rounds
wt_class_df.loc[wt_class_df['Bout'].str.contains("women's fly", case=False), 'Bout'] = "Women's Flyweight"
wt_class_df.loc[wt_class_df['Bout'].str.contains("women's feather", case=False), 'Bout'] = "Women's Featherweight"
wt_class_df.loc[wt_class_df['Bout'].str.contains("women's straw", case=False), 'Bout'] = "Women's Strawweight"
wt_class_df.loc[wt_class_df['Bout'].str.contains("women's bantam", case=False), 'Bout'] = "Women's Bantamweight"

# Change the name of men's rounds
wt_class_df.loc[wt_class_df['Bout'].str.contains('feather', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Featherweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('middle', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Middleweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('bantam', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Bantamweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('lightweight', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Lightweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('fly', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Flyweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('welter', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Welterweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('light heavy', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's", case=False, na=False), 'Bout'] = 'Light Heavyweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('heavyweight', case=False, na=False) &
    ~wt_class_df['Bout'].str.contains("women's|light", case=False, na=False), 'Bout'] = 'Heavyweight'
wt_class_df.loc[wt_class_df['Bout'].str.contains('catch', case=False), 'Bout'] = 'Catch Weight'

And once again make sure that the submissions have consistent names:

# Make sure we only have the fights that end in a sub
wt_class_df = wt_class_df[wt_class_df['Fight Method'] == 'Submission']
wt_class_df = wt_class_df.rename(columns={'Finish Details or Judges Scorecard':'Submission'})

# Make sure the submissions have consistent names
wt_class_df = wt_class_df.copy()
wt_class_df.loc[wt_class_df['Submission'].str.contains('rear naked', case=False), 'Submission'] = 'RNC'
wt_class_df.loc[wt_class_df['Submission'].str.contains('triangle', case=False), 'Submission'] = 'Triangle'
wt_class_df.loc[wt_class_df['Submission'].str.contains('armbar', case=False), 'Submission'] = 'Armbar'
wt_class_df.loc[wt_class_df['Submission'].str.contains('guillotine', case=False), 'Submission'] = 'Guillotine'
wt_class_df.loc[wt_class_df['Submission'].str.contains('kimura', case=False), 'Submission'] = 'Kimura'
wt_class_df.loc[wt_class_df['Submission'].str.contains('anaconda', case=False), 'Submission'] = 'Anaconda'
wt_class_df.loc[wt_class_df['Submission'].str.contains('heel hook', case=False), 'Submission'] = 'Heel Hook'
wt_class_df.loc[wt_class_df['Submission'].str.contains("d'arce", case=False), 'Submission'] = "D'Arce"
wt_class_df.loc[wt_class_df['Submission'].str.contains('choke|headlock|necktie', case=False), 'Submission'] = 'Other Choke'
wt_class_df.loc[wt_class_df['Submission'].str.contains('kneebar|calf|z-lock|ankle', case=False), 'Submission'] = 'Other Leglock'
wt_class_df.loc[wt_class_df['Submission'].str.contains('crank', case=False), 'Submission'] = 'Crank'

Once this is done, we find the submission count broken out by each weight class that were created above:

# Use so we can make all the rows and cols of a pandas df
weight_classes = wt_class_df['Bout'].unique()
all_subs = wt_class_df['Submission'].unique()
wgt_class_subs = pd.DataFrame(columns=weight_classes)

# Create a DataFrame with all possible submissions as the index
wgt_class_subs = pd.DataFrame(index=all_subs, columns=weight_classes)

# Go through each weight class and get the value counts for each sub
for weight_class in weight_classes:
  wgt_class_subs[weight_class] = wt_class_df[wt_class_df['Bout'] == weight_class]['Submission'].value_counts()

# Some subs don't appear in classes so change any NaN to 0
wgt_class_subs = wgt_class_subs.fillna(0)
wgt_class_subs = wgt_class_subs.astype(int)
wgt_class_subs.head()

Table 1

Table 1

Once we have these counts, let’s find the most common submission for each weight and then graph it to see what the results are:

# Get the sub that is most common in each weight class and then its value count
max_submissions = wgt_class_subs.idxmax()
max_counts = wgt_class_subs.max()

# Combine the results into df
max_wgt_class_subs = pd.DataFrame({'submission': max_submissions, 'count': max_counts})
# Create a bar chart
plt.figure(figsize=(10, 6))
ax = sns.barplot(x=max_wgt_class_subs.index, y='count',
                  data=max_wgt_class_subs,
                  palette='viridis',
                  hue=max_wgt_class_subs.index,  # use `x` variable for hue
                  dodge=False,                   # prevent stacking (for single-category bars)
                  legend=False)

for index, row in max_wgt_class_subs.iterrows():
  plt.text(
    index,                      # X position (center of the bar)
    row['count'] + 0.5,         # Y position (slightly above the bar)
    row['submission'],          # Text to display (value from 'submission' column)
    ha='center', fontsize=10    # Center alignment and font size
  )

# Add labels and title
plt.title('Counts of Most Frequent Sub by Weight Class', fontsize=16)
plt.xlabel('Weight Class', fontsize=12)
plt.ylabel('Count of Most Frequent Sub', fontsize=12)
plt.xticks(rotation=90)  # rotate x-axis labels for better readability
plt.tight_layout()

# Show the plot
plt.show()

Figure 2

Figure 2

From the data shown by Figure 2, our results are clear. When we break the fights out by weight class, the RNC is the most frequent submission in 100% of our categories. I’ll add the caveat that there can be other factors that contribute here, such as fighters often turning to get up, but the one thing I can say is this: be extra careful when someone has your back.


메타데이터
post_id
be2f3e3a89f7
slug
what-is-the-king-of-ufc-submissions-be2f3e3a89f7
url
https://medium.com/@dmanis13/what-is-the-king-of-ufc-submissions-be2f3e3a89f7
canonical_url
https://medium.com/@dmanis13/what-is-the-king-of-ufc-submissions-be2f3e3a89f7
author_url
https://medium.com/@dmanis13
status
ok
fetched_at
2026-06-26 12:24:55