Calculating and Interpreting Readability Metrics with Textstat

How to Calculate and Interpret Readability Metrics with Textstat

Textstat is a Python library used for calculating statistical measures from text. A useful tool for quantifying text complexity and readability, we introduced textstat and its basic functionality in a previous article. This time around we will move beyond the fundamentals and learn how to calculate and understand text readability scores with the library.

If you have yet to read the previous article, I suggest you start there first.

Readability Scores

First we will see how to get readability scores of text using textstat. Here is a function that will do just that.

import textstat
import pandas as pd
import matplotlib.pyplot as plt

def get_readability_scores(text):
    """Get all major readability metrics at once"""
    return {
        'flesch_ease': textstat.flesch_reading_ease(text),
        'flesch_grade': textstat.flesch_kincaid_grade(text),
        'gunning_fog': textstat.gunning_fog(text),
        'smog': textstat.smog_index(text),
        'ari': textstat.automated_readability_index(text),
        'coleman_liau': textstat.coleman_liau_index(text)
    }

We will also create a simple function for helping us quickly interpret the Flesch readability score.

def interpret_flesch(score):
    """Interpret Flesch score"""
    if score >= 90: return 'Very Easy - 5th grade'
    elif score >= 80: return 'Easy - 6th grade'
    elif score >= 70: return 'Fairly Easy - 7th grade'
    elif score >= 60: return 'Standard - 8th-9th grade'
    elif score >= 50: return 'Fairly Difficult - 10th-12th grade'
    elif score >= 30: return 'Difficult - College'
    else: return 'Very Difficult - College Graduate'

Let’s compare different types of writing:

texts = {
    'scientific': """The results demonstrate a statistically significant correlation 
                     between atmospheric carbon dioxide concentrations and global 
                     mean temperature anomalies (p < 0.001).""",
    
    'news': """Local officials announced today that the city's recycling program 
               will expand next month. Residents will receive new bins next week.""",
    
    'children': """The big red dog ran to the park. He saw a ball and started to 
                   play. His tail wagged happily."""
}

# Calculate scores for all texts
scores_df = pd.DataFrame({name: get_readability_scores(text) 
                         for name, text in texts.items()})

# Add interpretations
scores_df.loc['flesch_interpretation'] = scores_df.loc['flesch_ease'].apply(interpret_flesch)

print(scores_df)

And here is the output.

                                              scientific                      news               children
flesch_ease                                        -15.5                     61.33                  98.51
flesch_grade                                        20.1                       7.2                    1.2
gunning_fog                                        20.23                      10.0                   2.68
smog                                                 0.0                       0.0                    6.4
ari                                                 24.0                      10.2                   -1.0
coleman_liau                                       25.47                     13.14                   0.06
flesch_interpretation  Very Difficult - College Graduate  Standard - 8th-9th grade  Very Easy - 5th grade

Visualizing the Differences

Let's visualize the differences in our results. The following function can help accomplish this.

def plot_readability_comparison(df, metric='flesch_ease'):
    df.loc[metric].plot(kind='bar')
    plt.title(f'{metric.replace("_", " ").title()} by Text Type')
    plt.ylabel('Score')
    plt.tight_layout()
    plt.show()

And we could call the function like so:

plot_readability_comparison(scores_df)

And our output:

Visualizing the Differences

Tracking Readability Changes

Need to track readability over document sections? No problem! Here's how:

def analyze_text_sections(text, section_size=1000):
    """Analyze readability of text in sections"""
    # Split text into roughly equal sections
    words = text.split()
    sections = [' '.join(words[i:i + section_size]) 
               for i in range(0, len(words), section_size)]
    
    # Get scores for each section
    section_scores = [get_readability_scores(section) 
                     for section in sections]
    
    return pd.DataFrame(section_scores)

And here is how we could use this function:

# Load and analyze sample text
with open('sample.txt', 'r') as file:
    text = file.read()
    
# Analyze text in sections of 200 words
results = analyze_text_sections(text, section_size=200)
print(results)

And here is a sample result:

   flesch_ease  flesch_grade  gunning_fog  smog   ari  coleman_liau
0        75.71           5.8         9.00  10.4   6.8          8.28
1       -10.73          18.3        21.72  17.5  20.9         24.70
2       -33.43          22.9        26.40  21.0  25.7         27.90
3       -46.63          28.0        30.98  26.2  31.9         27.80

Common Pitfalls

Here are a few common pitfalls associated with finding and interpreting readability scores to keep an eye out for:

  1. Short texts (<100 words) give unreliable scores
  2. Technical terms artificially inflate scores
  3. Different formulas can vary significantly
  4. Formatting can affect scores

Wrapping Up

And that's all you need for basic readability analysis. You can use these tools to ensure your text matches your target audience. Keep an eye out for more information on how to use textstat for additional statistical analysis tasks in the near future.

Leave a Reply

Your email address will not be published. Required fields are marked *