Findings - Estilos de Escrita

Como P1 e P2 se expressam por escrito — vocabulário, pontuação, emojis

Author

Marlon

Published

May 7, 2026

Resumo

Cada pessoa escreve de um jeito. Esta análise compara os estilos de P1 e P2 ao longo dos ~10 meses de relacionamento (dez/2024–out/2025, 89.336 mensagens com conteúdo textual).

O padrão que emerge é claro e, curiosamente, assimétrico: P1 e P2 não escrevem o mesmo tanto, não usam os mesmos símbolos e não acentuam o mesmo tipo de emoção.

Note

P1 e P2 são identificadores anônimos dos dois remetentes da conversa. A atribuição foi feita pelo pipeline; nenhuma mensagem crua é exposta aqui sem filtro manual.


Setup

Code
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from pathlib import Path
from collections import Counter
import re
import emoji
import warnings
warnings.filterwarnings('ignore')

from whatsapp.pipeline.config import DATA_FOLDER
DATA_DIR = Path("../data/processed") / DATA_FOLDER

# Carrega dados enriquecidos + contexto de fase
df = pd.read_parquet(DATA_DIR / 'messages_enriched.parquet')
dfc = pd.read_parquet(DATA_DIR / 'messages_with_context.parquet')
df['fase_relacionamento'] = dfc['fase_relacionamento']

# Foco no período de Relacionamento (pós 16/dez/2024)
df = df[df['fase_relacionamento'] == 'Relacionamento'].copy()
df_texto = df[df['conteudo'].notna() & (df['conteudo'] != '')].copy()

print(f"Mensagens analisadas: {len(df):,}")
print(f"Com conteúdo textual: {len(df_texto):,}")
print(f"Período: {df['timestamp'].min().date()} → {df['timestamp'].max().date()}")
Mensagens analisadas: 89,336
Com conteúdo textual: 66,743
Período: 2024-12-16 → 2025-10-20

Volume e frequência

P1 envia 51,0% das mensagens; P2 envia 49,0%. A divisão é quase equilibrada, mas há assimetrias no como.

Code
vol = df.groupby('remetente').size().reset_index(name='msgs')
vol['pct'] = vol['msgs'] / vol['msgs'].sum() * 100
vol
remetente msgs pct
0 P1 45591 51.033178
1 P2 43745 48.966822

Ambos respondem rápido: 81% das mensagens de P1 e 81% das mensagens de P2 são classificadas como “resposta rápida” (< 5 minutos da mensagem anterior da outra pessoa). A conversa é fluida nos dois lados.


Tamanho das mensagens

A mediana é parecida — ~17 caracteres — mas P1 tem cauda mais longa (mensagem mais extensa chega a 8.561 caracteres, contra 3.030 de P2).

Code
fig = make_subplots(
    rows=1, cols=2,
    subplot_titles=('Distribuição do tamanho (caracteres, cap em 200)',
                    'Tamanho médio em palavras'),
    column_widths=[0.7, 0.3]
)

# Histograma por remetente
for who, cor in [('P1', '#4C78A8'), ('P2', '#F58518')]:
    sub = df_texto[(df_texto['remetente'] == who) & (df_texto['tamanho_caracteres'] <= 200)]
    fig.add_trace(
        go.Histogram(x=sub['tamanho_caracteres'], name=who, opacity=0.7,
                     marker_color=cor, nbinsx=50),
        row=1, col=1
    )

# Média palavras
palavras = df_texto[df_texto['tamanho_palavras'] > 0].groupby('remetente')['tamanho_palavras'].mean().reset_index()
fig.add_trace(
    go.Bar(x=palavras['remetente'], y=palavras['tamanho_palavras'],
           marker_color=['#4C78A8', '#F58518'], showlegend=False,
           text=palavras['tamanho_palavras'].round(1), textposition='outside'),
    row=1, col=2
)

fig.update_layout(
    height=400, barmode='overlay',
    title_text="Tamanho de mensagens — P1 vs P2",
    showlegend=True
)
fig.update_xaxes(title_text="Caracteres", row=1, col=1)
fig.update_yaxes(title_text="Frequência", row=1, col=1)
fig.update_yaxes(title_text="Palavras (média)", row=1, col=2)
fig.show()

Leitura: mensagens curtas (<50 caracteres) dominam em ambos. A grande maioria da conversa é trocada em rajadas curtas, não em parágrafos longos. P2 tem ligeira preferência por mensagens um pouco maiores em número de palavras (média 5,1 palavras vs. 5,4 de P1, mas mediana 4 vs. 3).


Emojis — o sinal mais claro de identidade

A maior surpresa da análise: emojis separam bem mais P1 e P2 do que o texto em si.

Quem usa mais emoji

Code
emoji_stats = df_texto.groupby('remetente').agg(
    pct_msgs_com_emoji=('tem_emoji', lambda x: x.mean() * 100),
    total_emojis=('qtd_emojis', 'sum'),
    media_quando_usa=('qtd_emojis', lambda x: x[x > 0].mean())
).round(2)
emoji_stats
pct_msgs_com_emoji total_emojis media_quando_usa
remetente
P1 10.53 3838 1.10
P2 4.07 1855 1.35

P1 usa emoji com o dobro de frequência — em ~7,6% das mensagens contra ~3,1% de P2. Mas quando P2 usa, tende a mandar mais emojis por mensagem (1,4 vs. 1,1 de P1).

Assinaturas de emoji

Aqui a divisão é quase caricata. Cada pessoa tem uma “assinatura” dominante.

Code
def extract_emojis(text):
    return [c for c in str(text) if c in emoji.EMOJI_DATA]

top_emojis = {}
for who in ['P1', 'P2']:
    sub = df_texto[df_texto['remetente'] == who]
    all_e = []
    for t in sub['conteudo']:
        all_e.extend(extract_emojis(t))
    # Filtra modificadores de pele (que vêm sozinhos como 🏻🏼)
    filtered = [e for e in all_e if e not in {'🏻', '🏼', '🏽', '🏾', '🏿', '▪'}]
    top_emojis[who] = Counter(filtered).most_common(10)

fig = make_subplots(rows=1, cols=2, subplot_titles=('Top emojis — P1', 'Top emojis — P2'))

for i, who in enumerate(['P1', 'P2'], start=1):
    items = top_emojis[who]
    emojis_list = [x[0] for x in items]
    counts = [x[1] for x in items]
    fig.add_trace(
        go.Bar(x=counts, y=emojis_list, orientation='h',
               marker_color='#4C78A8' if who == 'P1' else '#F58518',
               text=counts, textposition='outside', showlegend=False),
        row=1, col=i
    )

fig.update_layout(height=450, title_text="Top emojis por pessoa")
fig.update_xaxes(title_text="Ocorrências")
fig.update_yaxes(autorange='reversed')
fig.show()

Leitura:

  • P1 distribui entre vários emojis românticos/carinhosos e risadas: 😍 (632), 😂 (451), 😘 (323), 🥰 (297), 🖤 (214), 🫶 (206).
  • P2 é radicalmente concentrado: 💚 aparece 1.504 vezes — mais do que todos os emojis mais usados de P1 combinados. É uma assinatura pessoal forte, quase exclusiva (💚 é o emoji “verde” — possível referência à cor favorita ou apelido interno).

A diferença não é apenas de volume: é de variedade. P1 rotaciona entre carinhos; P2 repete o mesmo símbolo como carimbo.


Pontuação — “P1 pontua, P2 explode”

A forma como cada pessoa pontua também diverge:

Code
pont = df_texto.groupby('remetente').agg(
    pct_interrogacao=('tem_interrogacao', lambda x: x.mean() * 100),
    pct_exclamacao=('tem_exclamacao', lambda x: x.mean() * 100),
    pct_caixa_alta=('eh_caixa_alta', lambda x: x.mean() * 100)
).round(1)
pont
pct_interrogacao pct_exclamacao pct_caixa_alta
remetente
P1 7.6 1.5 3.0
P2 6.7 5.2 3.5

P2 usa quase 4x mais exclamações (4,0% das mensagens vs. 1,1% de P1). P1 e P2 perguntam em taxas semelhantes (~5% cada). Caixa alta também é mais frequente em P2 (2,7% vs. 2,2%).

Isso confirma o achado do notebook de EDA (05.3-eda-conteudo): P1 pontua, P2 explode. Onde P1 escreve calmo e pausado, P2 escreve com intensidade visível — exclamações, letras maiúsculas, kkkkk mais longos.


Vocabulário — o que cada um fala

Code
# Stopwords + ruído do WhatsApp
stops = {'a','o','e','de','da','do','que','é','em','um','uma','para','com','não',
         'por','os','as','no','na','se','mais','eu','te','tá','pra','ja','ai','aí',
         'mas','só','ta','q','vc','já','aqui','vai','ser','tem','como','bem','muito',
         'meu','minha','sim','tbm','nao','eh','pq','né','era','tava','esse','essa',
         'isso','la','foi','estava','entao','então','vou','quando','sei','nem','ao',
         'das','dos','tudo','teu','tua','tu','vcs','porque','to','estou','ne','oi',
         'oie','sou','message','this','edited','deleted'}

def top_words(textos, n=20):
    all_words = []
    for t in textos:
        text = ''.join(c for c in str(t).lower() if c not in emoji.EMOJI_DATA)
        text = re.sub(r'[^a-záàâãéêíóôõúüç\s]', ' ', text)
        words = [w for w in text.split() if len(w) >= 3 and w not in stops]
        all_words.extend(words)
    return Counter(all_words), len(all_words)

words_p1, total_p1 = top_words(df_texto[df_texto['remetente']=='P1']['conteudo'])
words_p2, total_p2 = top_words(df_texto[df_texto['remetente']=='P2']['conteudo'])

print(f"P1: {len(words_p1):,} palavras únicas | {total_p1:,} tokens")
print(f"P2: {len(words_p2):,} palavras únicas | {total_p2:,} tokens")
print(f"\nDiversidade (únicas/tokens): P1={len(words_p1)/total_p1*100:.1f}% | P2={len(words_p2)/total_p2*100:.1f}%")
P1: 12,178 palavras únicas | 99,476 tokens
P2: 10,694 palavras únicas | 93,918 tokens

Diversidade (únicas/tokens): P1=12.2% | P2=11.4%

P1 tem vocabulário mais amplo (12.179 palavras únicas vs. 10.695 de P2), mas a densidade relativa é parecida. A diferença qualitativa está quais palavras dominam.

Code
TOP_N = 15
tp1 = words_p1.most_common(TOP_N)
tp2 = words_p2.most_common(TOP_N)

fig = make_subplots(rows=1, cols=2, subplot_titles=(f'Top {TOP_N} palavras — P1', f'Top {TOP_N} palavras — P2'))

for i, (who, items) in enumerate([('P1', tp1), ('P2', tp2)], start=1):
    words = [x[0] for x in items]
    counts = [x[1] for x in items]
    fig.add_trace(
        go.Bar(x=counts, y=words, orientation='h',
               marker_color='#4C78A8' if who == 'P1' else '#F58518',
               text=counts, textposition='outside', showlegend=False),
        row=1, col=i
    )

fig.update_layout(height=550, title_text="Palavras mais usadas (sem stopwords)")
fig.update_xaxes(title_text="Ocorrências")
fig.update_yaxes(autorange='reversed')
fig.show()

Palavras em comum (top 15): amor, bom, dia, acho, demais, ver, amo. O núcleo afetivo e cotidiano é compartilhado.

Distintivas de cada um:

  • P1: gata, baby, linda, hahaha, nossa, você (com acento). Vocabulário mais verbalizado, com apelidos carinhosos.
  • P2: gato, mto (muito abreviado), ele/ela, queria, gostoso, kkkkk (em diferentes comprimentos), agora. Mais abreviado, mais temporal (“agora”), mais narrativo sobre terceiros (“ele/ela”).

Curiosidade: os apelidos se espelham — P1 chama P2 de “gata”; P2 chama P1 de “gato”.


Ritmo — como cada um quebra pensamento

Code
seq = df_texto.groupby('remetente')['sequencia_mesmo_remetente'].agg(
    media_seq='mean',
    max_seq='max'
).round(2)
seq
media_seq max_seq
remetente
P1 3.44 54
P2 3.21 32

A coluna sequencia_mesmo_remetente mede quantas mensagens consecutivas a mesma pessoa envia antes da outra responder. Valores maiores indicam hábito de “mandar em rajadas” em vez de consolidar numa mensagem só.

Ambos têm padrão similar — escrevem em pensamentos curtos enviados em sequência, não em textões.


Conclusões

  1. Volume é equilibrado, mas estilo é assimétrico. 51% vs. 49% de mensagens, com divergências visíveis em todo o resto.

  2. P1 é o “emoji-diverso”; P2 é o “emoji-fiel”. P1 rotaciona entre 😍 😂 😘 🥰 🖤 🫶; P2 carimba 💚 em praticamente toda mensagem com emoji (1.504 usos — um volume de assinatura única).

  3. P2 é visivelmente mais enfático na escrita. Quase 4x mais exclamações e uso mais frequente de caixa alta. “P1 pontua, P2 explode.”

  4. Vocabulário compartilha o núcleo afetivo, diverge na textura. amor, bom, dia em comum; P1 verbaliza com apelidos (“gata”, “linda”, “baby”), P2 abrevia e narra (“mto”, “queria”, “agora”).

  5. Apelidos simétricos. P1 chama P2 de “gata”; P2 chama P1 de “gato”.

  6. Ambos escrevem em rajadas curtas, não em textões. Mediana de 3-4 palavras por mensagem, 81% das mensagens são resposta rápida.

TipVer também