Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ node_modules/
# testing
/output
/vector_data
test.py
test.py
test.ipynb
58 changes: 54 additions & 4 deletions src/team_comm_tools/features/keywords.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# reference: https://github.com/bbevis/politenessPy/blob/main/keywords.py
kw = {
"spacy_neg_only": {
"Negative_Emotion": [
Expand Down Expand Up @@ -7260,7 +7261,34 @@
" sorry ",
" woops ",
" whoops ",
" oops "
" oops ",
" apology "
],
"Third_Person": [
" he ",
" him ",
" his ",
" himself ",
" she ",
" her ",
" hers ",
" herself ",
" they ",
" them ",
" their ",
" theirs ",
" themselves "
],
"Contrast_Conjunction": [
" but ",
" however ",
" instead ",
" although ",
" even though ",
" despite ",
" and yet ",
" nevertheless ",
" nonetheless "
],
"Ask_Agency": [
" do me a favor ",
Expand Down Expand Up @@ -7365,7 +7393,7 @@
"Gratitude": [
" thank ",
" thanks ",
" thank you ",
#" thank you ",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct that we're commenting this out because otherwise we'd double count "thank you very much" ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Both "thank" and "thank you" will be counted. We discussed this issue with Burint and he made the changes.

" grateful ",
" gratitude ",
" cheers "
Expand Down Expand Up @@ -14419,25 +14447,47 @@
" cock ",
" crap ",
" damn ",
" dammit ",
" damnit ",
" dick ",
" dickhead ",
" dick-head ",
" dumb ",
" dumbass ",
" dumb-ass ",
" dumb ass ",
" dyke ",
" fuck ",
" fucking ",
" fucker ",
" goddam ",
" goddammit ",
" goddamed ",
" hell ",
" horshit ",
" homo ",
" jackass ",
" jackass ",
" motherfucker ",
" mother-fucker ",
" motherfucking ",
" nigger ",
" nigra ",
" piss ",
" prick ",
" pussy ",
" queer ",
" screw ",
" shit ",
" shite ",
" shitting ",
" sob ",
" sonofa ",
" suck ",
" sucked ",
" sucks "
" sucks ",
" twat ",
" wanker ",
" whore "
],
"Truth_Intensifier": [
" really ",
Expand Down
92 changes: 74 additions & 18 deletions src/team_comm_tools/features/politeness_v2_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,29 +218,85 @@ def bare_command(doc):
def Question(doc):
"""
Counts the number of sentences containing question words and question marks.

Reference: https://github.com/bbevis/politenessPy/blob/main/strategy_extractor.py
Args:
doc (spacy.tokens.Doc): The spaCy Doc object containing the text to be analyzed.

Returns:
tuple: A tuple containing the counts of Yes/No questions and WH-questions.
"""

keywords = set([' who ', ' what ', ' where ', ' when ', ' why ', ' how ', ' which '])
tags = set(['WRB', 'WP', 'WDT'])

# doc = nlp(text)
sentences = [str(sent) for sent in doc.sents if '?' in str(sent)]
all_qs = len(sentences)

n = 0
for i in range(len(sentences)):
whq = [token.tag_ for token in nlp(sentences[i]) if token.tag_ in tags]

if len(whq) > 0:
n += 1

return all_qs - n, n
# POS tags for WH-words like who/what/where
search_tags = {'WRB', 'WP', 'WDT'}
# WH-words and common auxiliaries that follow them in real questions
wh_words = {'what', 'who', 'where', 'when', 'why', 'how', 'which'}
wh_followers = {
'what': {'are', 'is', 'do', 'does', 'can', 'should', 'might'},
'who': {'is', 'are', 'was', 'can', 'should'},
'where': {'is', 'are', 'can', 'should'},
'when': {'is', 'are', 'can', 'should'},
'why': {'is', 'are', 'do', 'does', 'can', 'might', 'would'},
'how': {'is', 'are', 'do', 'does', 'can', 'should', 'would'},
'which': {'is', 'are', 'was', 'can', 'should'}
}
# Auxiliaries that typically initiate Yes/No questions
yesno_aux = {'do', 'does', 'did', 'have', 'has', 'had',
# 'can', 'could', 'will', 'would',
'may', 'might', 'shall', 'should',
'is', 'are', 'was', 'were', 'am'}
# Pronouns that often follow auxiliaries in Yes/No questions
pronoun_followers = {'i', 'you', 'we', 'he', 'she', 'they', 'it'}
# filler_words = {'ok', 'so', 'well', 'like', 'you know', 'i mean', 'actually', 'basically', 'right', 'just', 'uh', 'um', 'oh', 'hmm', 'like'}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we no longer ignoring filler words like 'ok so' ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This was my own solution to this test case: "so Which part should we do first?". Burint updated his code later, so I adapted that instead.


wh_count = 0
yesno_count = 0
counted_sentences = set()
for sent in doc.sents:
sent_text = sent.text.strip()
sent_tokens = list(sent)
if not sent_tokens:
continue
# Method 1: Find question sentences by checking for '?' at end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there's a bug with this logic, which is that, if the sentence doesn't end with a question mark, the fallback method is very error prone. For example:

Question(nlp("can you tell me what is your name?")) yields YesNo = 1, WH = 0 (correct)
Question(nlp("can you tell me what is your name")) yields YesNo = 0, WH = 1 (incorrect)

Why can't we use logic that looks for the question words, but then applies the logic with the search_tags, which helps to get around some of these problems?

if sent_text.endswith('?'):
# try to find the first WH-word in the sentence
wh = False
for token in sent_tokens:
if token.text.lower() in wh_words and token.tag_ in search_tags and token.dep_ not in {"relcl", "acl"}\
and token.i < sent.root.i:
wh = True
break
if wh:
wh_count += 1
else:
# Fallback: no WH in the sentence → treat as Yes/No question
yesno_count += 1
counted_sentences.add(sent.start)
continue
# Method 2: For remaining sentences, apply lexical rule-based detection --- Extract tokens and their metadata for fast access
for i in range(len(sent_tokens) - 1):
tok1 = sent_tokens[i]
tok2 = sent_tokens[i + 1]
t1_lower = tok1.text.lower()
t2_lower = tok2.text.lower()
if sent.start in counted_sentences:
break # already counted
# WH pattern
if t1_lower in wh_words and t2_lower in wh_followers.get(t1_lower, set()):
wh_count += 1
counted_sentences.add(sent.start)
break
# Yes/No pattern
if t1_lower in yesno_aux and t2_lower in pronoun_followers:
yesno_count += 1
counted_sentences.add(sent.start)
break
return yesno_count, wh_count
# sentences = [str(sent) for sent in doc.sents if '?' in str(sent)]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the old logic commented out, which we can remove once we establish the new logic works!

# all_qs = len(sentences)
# n = 0
# for i in range(len(sentences)):
# whq = [token.tag_ for token in nlp(sentences[i]) if token.tag_ in tags]
# if len(whq) > 0:
# n += 1
# return all_qs - n, n


def word_start(keywords, doc):
Expand Down
8 changes: 7 additions & 1 deletion tests/data/cleaned_data/test_chat_level.csv
Original file line number Diff line number Diff line change
Expand Up @@ -1437,11 +1437,17 @@ yeomans_test,yeomans_user_b,I guess they almost complete it.,Hedges_receptivenes
yeomans_test,yeomans_user_a,"Ma'am, no my lady, do you know Mr. Smith?",Formal_Title_receptiveness_yeomans,2
yeomans_test,yeomans_user_b,"I agree, this is correct",Agreement_receptiveness_yeomans,2
yeomans_test,yeomans_user_a,This is for you. Why you don't understand it's for you?,For_You_receptiveness_yeomans,2
yeomans_test,yeomans_user_b,"Thank you, I'm really grateful",Gratitude_receptiveness_yeomans,3
yeomans_test,yeomans_user_b,"Thank you, I'm really grateful",Gratitude_receptiveness_yeomans,2
yeomans_test,yeomans_user_a,We here you. We totally understand,Acknowledgement_receptiveness_yeomans,2
yeomans_test,yeomans_user_b,"Shit. You dumb asshole, what the hell? Who's that bastard? Suck my dick.",Swearing_receptiveness_yeomans,7
yeomans_test,yeomans_user_a,"Hey hello good morning, oh actually good evening.",Hello_receptiveness_yeomans,4
yeomans_test,yeomans_user_b,Are you sure? Is this the guy? Did he lie to you?,YesNo_Questions_receptiveness_yeomans,3
yeomans_test,yeomans_user_a,"Did you finish the report, which was due today?",YesNo_Questions_receptiveness_yeomans,1
yeomans_test,yeomans_user_b,"Did you finish the report, which was due today?",WH_Questions_receptiveness_yeomans,0
yeomans_test,yeomans_user_a,"We can start here. What is the question?",WH_Questions_receptiveness_yeomans,1
yeomans_test,yeomans_user_b,"Has she met the teacher who helped you last year?",WH_Questions_receptiveness_yeomans,0
yeomans_test,yeomans_user_a,"Do you know what time it is?",WH_Questions_receptiveness_yeomans,0
yeomans_test,yeomans_user_b,"Have you read the article that explains why this happens?",WH_Questions_receptiveness_yeomans,0
yeomans_test,yeomans_user_a,I'm sorry I sincerely apologize.,Apology_receptiveness_yeomans,2
yeomans_test,yeomans_user_b,Wow! Amazing! Perfect!,Affirmation_receptiveness_yeomans,3
yeomans_test,yeomans_user_a,I love you. My friend,First_Person_Single_receptiveness_yeomans,2
Expand Down