Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import spacy | |
| import pandas as pd | |
| import networkx as nx | |
| import matplotlib.pyplot as plt | |
| # Initialize spaCy NLP model | |
| nlp = spacy.load("en_core_web_sm") | |
| # Import Lexicon | |
| cues = pd.read_excel('link_cues.xlsx') | |
| list_causalmarkers = cues['causal_markers'] | |
| # def contains_words_or_phrases(words_list, sentence): | |
| # """ | |
| # Check if any word or phrase from words_list is present in the sentence. | |
| # :param words_list: List of words or phrases to check | |
| # :param sentence: The input sentence where to look for words or phrases | |
| # :return: Entities if any word or phrase is found, otherwise None | |
| # """ | |
| # # Normalize the sentence to lower case to make the search case insensitive | |
| # normalized_sentence = sentence.lower() | |
| # # Check each word or phrase in the list | |
| # for word_or_phrase in words_list: | |
| # # Check if the word or phrase is in the normalized sentence | |
| # if word_or_phrase.lower() in normalized_sentence: | |
| # return True # Return True immediately if any word or phrase is found | |
| # return False # Return False if none of the words or phrases are found | |
| def contains_words_or_phrases(words_list, sentence): | |
| """ | |
| Check if any word or phrase from words_list is present in the sentence. | |
| :param words_list: List of words or phrases to check | |
| :param sentence: The input sentence where to look for words or phrases | |
| :return: True if any word or phrase is found, otherwise False | |
| """ | |
| # Process the sentence with spaCy to obtain the lemmatized form of each token | |
| processed_sentence = nlp(sentence.lower()) | |
| lemmatized_sentence = " ".join(token.lemma_ for token in processed_sentence) | |
| # Process each word or phrase for lemmatization | |
| for word_or_phrase in words_list: | |
| # Process and create a lemma string for the word or phrase | |
| processed_word_or_phrase = nlp(word_or_phrase.lower()) | |
| lemmatized_word_or_phrase = " ".join(token.lemma_ for token in processed_word_or_phrase) | |
| # Check if the lemmatized word or phrase is in the lemmatized sentence | |
| if lemmatized_word_or_phrase in lemmatized_sentence: | |
| return True # Return True immediately if any word or phrase is found | |
| return False # Return False if none of the words or phrases are found | |
| class NounExtractor: | |
| def __init__(self, nlp): | |
| """ | |
| Initialize the NounExtractor with a pre-loaded spaCy NLP model. | |
| """ | |
| self.nlp = nlp | |
| def process_text(self, text): | |
| """ | |
| Process the text using the spaCy NLP pipeline. | |
| """ | |
| return self.nlp(text) | |
| def get_noun_phrases(self, doc): | |
| """ | |
| Extract and refine noun phrases from the spaCy doc, tracking and using dependency labels accurately. | |
| """ | |
| noun_phrases = list(doc.noun_chunks) | |
| merged_phrases = [] | |
| skip_indexes = set() # Indexes to skip because they have been merged into another phrase | |
| list_dep_labels = [token.dep_ for token in doc] # List of dependency labels for each token | |
| for i in range(len(noun_phrases)): | |
| if i in skip_indexes: | |
| continue | |
| current = noun_phrases[i] | |
| # Collect dependency labels for the current noun phrase | |
| deps_in_phrase = {list_dep_labels[tok.i] for tok in current} | |
| # Merge logic based on 'phrases connectors' construction | |
| if i + 1 < len(noun_phrases) and (doc[current.end].text in ['of', 'in', 'among', 'on', 'towards', 'to', 'for', 'across']): | |
| next_phrase = noun_phrases[i + 1] | |
| if i + 2 < len(noun_phrases) and doc[next_phrase.end].dep_ == 'pcomp': | |
| extended_phrase = doc[current.start:noun_phrases[i + 2].end] | |
| skip_indexes.update({i + 1, i + 2}) | |
| extended_deps = {list_dep_labels[tok.i] for tok in extended_phrase} | |
| dep_label = self.determine_dep_label(extended_deps) | |
| merged_phrases.append((extended_phrase.text, dep_label)) | |
| continue | |
| else: | |
| merged_phrase = doc[current.start:next_phrase.end] | |
| skip_indexes.add(i + 1) | |
| merged_deps = {list_dep_labels[tok.i] for tok in merged_phrase} | |
| dep_label = self.determine_dep_label(merged_deps) | |
| merged_phrases.append((merged_phrase.text, dep_label)) | |
| continue | |
| if i not in skip_indexes: | |
| dep_label = self.determine_dep_label(deps_in_phrase) | |
| merged_phrases.append((current.text, dep_label)) | |
| return merged_phrases | |
| def determine_dep_label(self, deps_in_phrase): | |
| """ | |
| Determine the most appropriate dependency label for a phrase based on internal dependencies. | |
| """ | |
| if 'nsubj' in deps_in_phrase: | |
| return 'ROOTnsubj' | |
| elif 'nsubjpass' in deps_in_phrase: | |
| return 'ROOTnsubjpass' | |
| else: | |
| # Choose a representative dependency if no clear subject is present | |
| return deps_in_phrase.pop() if deps_in_phrase else 'unknown' | |
| def extract(self, sentence, causative_verb): | |
| """ | |
| Extracts and returns noun phrases with their detailed dependency tags from the sentence. | |
| """ | |
| doc = self.process_text(sentence) | |
| noun_phrases = self.get_noun_phrases(doc) | |
| result_dict = {phrase: dep for phrase, dep in noun_phrases} | |
| # Check for the presence of causative verbs like 'cause', in the sentence | |
| found_verbs = [v for v in causative_verb if v.lower() in sentence.lower()] | |
| if found_verbs: | |
| # Adjust dependency labels for noun phrases based on the presence of an causative verb. | |
| for phrase, dep in list(result_dict.items()): # Work on a copy of items to safely modify the dict | |
| if dep == 'ROOTnsubj': | |
| result_dict[phrase] = 'dobj' | |
| elif dep == 'dobj': | |
| result_dict[phrase] = 'ROOT' | |
| return result_dict | |
| def format_results(results): | |
| formatted = [] | |
| # Find all roots or central subjects to structure the phrases around them | |
| root_keys = [key for key, value in results.items() if value == 'ROOTnsubj' or value == 'ROOTnsubjpass'] | |
| for key, value in results.items(): | |
| if key in root_keys: | |
| continue # Skip the roots themselves when adding to the formatted list | |
| for root_key in root_keys: | |
| if value == 'ROOTnsubjpass': # If the dependency indicates a passive subject | |
| formatted.append(f"{key} -> {root_key}") | |
| else: | |
| formatted.append(f"{root_key} <- {key}") | |
| # Remove duplicates and return the formatted results | |
| formatted = list(set(formatted)) | |
| return formatted | |
| def wrap_label(label): | |
| """Helper function to wrap labels after every three words.""" | |
| words = label.split() | |
| wrapped_label = '\n'.join(' '.join(words[i:i+3]) for i in range(0, len(words), 3)) | |
| return wrapped_label | |
| def visualize_cognitive_map(formatted_results): | |
| G = nx.DiGraph() # Directed graph to show direction of relationships | |
| # Add edges based on formatted results | |
| for result in formatted_results: | |
| if '<-' in result: | |
| # Extract nodes and add edge in the reverse direction | |
| nodes = result.split(' <- ') | |
| G.add_edge(nodes[1], nodes[0]) | |
| elif '->' in result: | |
| # Extract nodes and add edge in the specified direction | |
| nodes = result.split(' -> ') | |
| G.add_edge(nodes[0], nodes[1]) | |
| # Position nodes using the spring layout | |
| pos = nx.spring_layout(G, k=0.50) | |
| # Setup the plot with a larger size | |
| plt.figure(figsize=(12, 8)) # Larger figure size for better visibility | |
| # Prepare custom labels with wrapped text | |
| labels = {node: wrap_label(node) for node in G.nodes()} | |
| # Draw the graph with custom labels | |
| nx.draw(G, pos, labels=labels, node_color='skyblue', edge_color='#FF5733', | |
| node_size=5000, font_size=10, font_weight='bold', with_labels=True, arrowstyle='-|>', arrowsize=30) | |
| plt.show() | |
| return plt | |
| # Use the functions | |
| extractor = NounExtractor(nlp=nlp) | |
| # Example of how to use this function | |
| # words_list = ["so", "because", "contribute", "due to", "therefore", "as"] | |
| causative_verb = ['affect', 'influence', 'increase', 'against', 'bring', 'lead', 'cause', 'is to', 'was to', | |
| 'are to', "were to", 'contribute', 'make', 'attribute', 'provoke'] | |
| # Define the callback function for the GUI | |
| def CogMapAnalysis(text): | |
| if contains_words_or_phrases(list_causalmarkers, text): | |
| result = extractor.extract(text, causative_verb) | |
| formatted_result = format_results(result) | |
| plot = visualize_cognitive_map(formatted_result) | |
| return formatted_result, plot | |
| else: | |
| formatted_result = "❌ No causal expression was identified." | |
| plot = None # Use None instead of empty string for non-existent objects | |
| return formatted_result, plot | |
| # Create the GUI using the 'gr' library | |
| with gr.Blocks() as demo: | |
| gr.Markdown('# CogMApp') | |
| gr.Markdown('### Generate cognitive maps from text with one click!') | |
| with gr.Row(): | |
| with gr.Column(): | |
| inputs = gr.Textbox(label="Input", lines=2, placeholder="Enter your text here...") | |
| submit_button = gr.Button("Submit") | |
| with gr.Column(): | |
| examples = gr.Examples(examples=[ | |
| "Public support for anti-discrimination laws and the movement to support immigrants grew due to the impact of getting widespread education on social justice issues.", | |
| "The introduction of new anti-discrimination laws has been driven by an increasing awareness of social injustices and grassroots movements.", | |
| "The weak law enforcement in this country is due to its citizens's ignorance.", | |
| "CogMApp is a tool that lets you create cognitive maps from text.", | |
| "The protests across the country are caused by the announcement of the new regulation." | |
| ], inputs=inputs) | |
| with gr.Row(): | |
| output = gr.Textbox(label="Result", lines=5, placeholder=" ") | |
| with gr.Row(): | |
| cogmap_plot = gr.Plot(label="Generated Cognitive Map") | |
| with gr.Row(): | |
| gr.Markdown("⚠️ Feel free to flag me if you find any errors. 🙂") | |
| with gr.Column(): | |
| gr.Markdown('Demo made with ❤ by P.K. Ningrum (2024) | Contact: [https://ningrumdaud.github.io/](https://ningrumdaud.github.io/)') | |
| # Set up the button to execute the function when clicked | |
| submit_button.click(CogMapAnalysis, inputs=[inputs], outputs=[output, cogmap_plot]) | |
| if __name__ == "__main__": | |
| demo.launch(show_api=False) | |