Skip to content
Back to Blog
2 min read

Analyzing Text with Azure Cognitive Services Text Analytics

A retail client this week handed me six months of customer feedback in a CSV and asked the question I get every couple of months: “what are people actually saying?” Reading 8,000 free-text responses by hand is not happening. Text Analytics is the boring-but-effective answer for this kind of work — sentiment, key phrases, entities, with no model to train and no GPU to babysit. A walkthrough of the bits I actually use.

Features Available

Text Analytics currently offers:

  • Sentiment Analysis - Detect positive, negative, or neutral sentiment
  • Key Phrase Extraction - Identify main talking points
  • Language Detection - Identify the language of input text
  • Named Entity Recognition - Extract entities like people, places, organizations

Setting Up the Service

# Create a Cognitive Services resource
az cognitiveservices account create \
    --name text-analytics-demo \
    --resource-group rg-cognitive \
    --kind TextAnalytics \
    --sku S \
    --location australiaeast \
    --yes

# Get the key
az cognitiveservices account keys list \
    --name text-analytics-demo \
    --resource-group rg-cognitive

Using the .NET SDK

dotnet add package Azure.AI.TextAnalytics

Sentiment Analysis

using Azure;
using Azure.AI.TextAnalytics;

public class TextAnalyticsService
{
    private readonly TextAnalyticsClient _client;

    public TextAnalyticsService(string endpoint, string key)
    {
        var credentials = new AzureKeyCredential(key);
        _client = new TextAnalyticsClient(new Uri(endpoint), credentials);
    }

    public async Task<SentimentResult> AnalyzeSentimentAsync(string text)
    {
        var response = await _client.AnalyzeSentimentAsync(text);
        var sentiment = response.Value;

        return new SentimentResult
        {
            Sentiment = sentiment.Sentiment.ToString(),
            PositiveScore = sentiment.ConfidenceScores.Positive,
            NeutralScore = sentiment.ConfidenceScores.Neutral,
            NegativeScore = sentiment.ConfidenceScores.Negative,
            Sentences = sentiment.Sentences.Select(s => new SentenceSentiment
            {
                Text = s.Text,
                Sentiment = s.Sentiment.ToString()
            }).ToList()
        };
    }
}

public class SentimentResult
{
    public string Sentiment { get; set; }
    public double PositiveScore { get; set; }
    public double NeutralScore { get; set; }
    public double NegativeScore { get; set; }
    public List<SentenceSentiment> Sentences { get; set; }
}

public class SentenceSentiment
{
    public string Text { get; set; }
    public string Sentiment { get; set; }
}

Batch Processing

For efficiency, process multiple documents in batches:

public async Task<List<SentimentResult>> AnalyzeBatchAsync(List<string> documents)
{
    var response = await _client.AnalyzeSentimentBatchAsync(documents);

    return response.Value
        .Where(r => !r.HasError)
        .Select(r => new SentimentResult
        {
            Sentiment = r.DocumentSentiment.Sentiment.ToString(),
            PositiveScore = r.DocumentSentiment.ConfidenceScores.Positive,
            NeutralScore = r.DocumentSentiment.ConfidenceScores.Neutral,
            NegativeScore = r.DocumentSentiment.ConfidenceScores.Negative
        })
        .ToList();
}

Key Phrase Extraction

public async Task<List<string>> ExtractKeyPhrasesAsync(string text)
{
    var response = await _client.ExtractKeyPhrasesAsync(text);
    return response.Value.ToList();
}

// Example usage
var text = "Azure Cognitive Services provides AI capabilities for developers. " +
           "The Text Analytics API offers sentiment analysis and key phrase extraction.";

var keyPhrases = await service.ExtractKeyPhrasesAsync(text);
// Returns: ["Azure Cognitive Services", "AI capabilities", "developers",
//           "Text Analytics API", "sentiment analysis", "key phrase extraction"]

Named Entity Recognition

public async Task<List<EntityResult>> RecognizeEntitiesAsync(string text)
{
    var response = await _client.RecognizeEntitiesAsync(text);

    return response.Value.Select(e => new EntityResult
    {
        Text = e.Text,
        Category = e.Category.ToString(),
        SubCategory = e.SubCategory,
        ConfidenceScore = e.ConfidenceScore
    }).ToList();
}

public class EntityResult
{
    public string Text { get; set; }
    public string Category { get; set; }
    public string SubCategory { get; set; }
    public double ConfidenceScore { get; set; }
}

Language Detection

public async Task<DetectedLanguage> DetectLanguageAsync(string text)
{
    var response = await _client.DetectLanguageAsync(text);
    var language = response.Value;

    return new DetectedLanguage
    {
        Name = language.Name,
        IsoCode = language.Iso6391Name,
        ConfidenceScore = language.ConfidenceScore
    };
}

Practical Application: Customer Feedback Analysis

public class FeedbackAnalyzer
{
    private readonly TextAnalyticsService _textAnalytics;

    public async Task<FeedbackAnalysis> AnalyzeFeedbackAsync(string feedback)
    {
        var sentimentTask = _textAnalytics.AnalyzeSentimentAsync(feedback);
        var keyPhrasesTask = _textAnalytics.ExtractKeyPhrasesAsync(feedback);
        var entitiesTask = _textAnalytics.RecognizeEntitiesAsync(feedback);

        await Task.WhenAll(sentimentTask, keyPhrasesTask, entitiesTask);

        return new FeedbackAnalysis
        {
            OriginalText = feedback,
            Sentiment = sentimentTask.Result,
            KeyPhrases = keyPhrasesTask.Result,
            Entities = entitiesTask.Result
        };
    }
}

Things to know before shipping this

  • It’s per-record billing, not per-character. Long documents get split. Cheap on small samples, surprisingly not-cheap when you point it at a year of support tickets without thinking.
  • The free tier (5,000 transactions/month) is enough to demo and prototype. Enough to convince stakeholders it’s worth doing properly.
  • Sentiment confidence scores beat the label. A “neutral” document at 0.45/0.45/0.10 is interesting in a way “neutral” alone isn’t. Always log all three confidences.
  • Don’t use NER as your primary entity store. It will hallucinate “Apple” as a person sometimes, and miss internal product names entirely. It’s a starting point, not a source of truth.

For “what are customers saying about us” dashboards, this is genuinely 80% of what you need. The remaining 20% — domain-specific entities, custom intents — is where you graduate to LUIS or roll your own model. But you don’t start there. You start here.\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.