<jupyter_start><jupyter_text>To a human reader, the understanding would be 'vinculin' has negligible influence on 'actin' related characteristics, which is different from 'talin'-related behaviour.The reason is the phrase 'negligible influence' between 'vinculin' and 'actin' indicates there is NO interaction between two while the previous observations between 'vinculin' and 'talin' contrasts with such.So I am doing here is develop a way to uncover this heuristic using machine learning model.  Analyse the Sentence Structure based on Stanza's Tokenized Output Approach 1: Using stanza's tokenizer instead of providing my own pretokenized sentence as input<jupyter_code>import stanza
nlp = stanza.Pipeline(lang='en', package='craft', processor='tokenize,pos')
doc = nlp(sample)
print(*[f'id: {word.id}\tword: {word.text.ljust(25, " ")}\tupos: {word.upos.ljust(6, " ")}\thead: {sent.words[word.head-1].text.ljust(25, " ") if word.head > 0 else "root"}\tdeprel: {word.deprel}' for sent in doc.sentences for word in sent.words], sep='\n')

%time
G = nx.Graph() # using undirected graph to establish a baseline behavior
for sent in doc.sentences:
    for word in sent.words:
        head = word.text
        tail = sent.words[word.head-1].text
        if word.upos == 'PUNCT' or sent.words[word.head-1].upos=='PUNCT':
            pass
        else:
            
            G.add_node(head, pos=word.upos, deprel=word.deprel)
            G.add_node(tail, pos=sent.words[word.head-1].upos, deprel=sent.words[word.head-1].deprel)
            G.add_edge(head, tail, weight=1)
bmt.plot_network(G)
# Let's examine if 'actin', 'talin', and 'vinculin' exist as distinct nodes
entities = ['actin','talin','vinculin']
nodes = G.nodes()
atoms = []
for node in nodes:
    for entity in entities:
        if entity in node:
            atoms.append(node)
atoms
from itertools import permutations
tuples = permutations(atoms, 2)
for t in tuples:
    print (t)
%time
tuples = permutations(atoms, 2)
simple_path = []
for t in tuples:
    #print (t)
    paths = list(nx.all_simple_paths(G, source=t[0], target=t[1]))
    for p in paths:
        #print (p)
        simple_path.append(p)
for index, p in enumerate(simple_path):
    print (index, ':', p)<jupyter_output>0 : ['vinculin', 'has', 'influence', 'dynamics', 'filament', 'actin']
1 : ['vinculin', 'has', 'influence', 'dynamics', 'stiffness', 'and', 'binding', 'protein', 'actin']
2 : ['vinculin', 'has', 'influence', 'dynamics', 'stiffness', 'actin']
3 : ['vinculin', 'has', 'influence', 'contrasts', 'observations', 'with', 'talin', 'protein', 'actin']
4 : ['vinculin', 'has', 'influence', 'contrasts', 'observations', 'with', 'talin', 'protein', 'binding', 'and', 'stiffness', 'actin']
5 : ['vinculin', 'has', 'influence', 'contrasts', 'observations', 'with', 'talin', 'protein', 'binding', 'and', 'stiffness', 'dynamics', 'filament', 'actin']
6 : ['vinculin', 'has', 'influence', 'contrasts', 'observations', 'talin', 'protein', 'actin']
7 : ['vinculin', 'has', 'influence', 'contrasts', 'observations', 'talin', 'protein', 'binding', 'and', 'stiffness', 'actin']
8 : ['vinculin', 'has', 'influence', 'contrasts', 'observations', 'talin', 'protein', 'binding', 'and', 'stiffness', 'dynamics', 'filament', 'a[...]<jupyter_text>From above simple paths, the relationship among those entities seems to be obscure to establish even there are over 100 ways to 'connect' any two assigned entities. Approach 2: Using pretokenized sentence as input<jupyter_code>pretokenized_nlp = stanza.Pipeline(lang='en', package='craft', processor='tokenize', tokenize_pretokenized=True)
doc_re = bmt.tokenizer(pretokenized_nlp, sample)
print(*[f'id: {word.id}\tword: {word.text.ljust(25, " ")}\tupos: {word.upos.ljust(6, " ")}\thead: {sent.words[word.head-1].text.ljust(25, " ") if word.head > 0 else "root"}\tdeprel: {word.deprel}' for sent in doc_re.sentences for word in sent.words], sep='\n')

%time
H = nx.Graph() # using undirected graph to establish a baseline behavior
for sent in doc_re.sentences:
    for word in sent.words:
        head = word.text
        tail = sent.words[word.head-1].text
        if word.upos == 'PUNCT' or sent.words[word.head-1].upos=='PUNCT':
            pass
        else:
            
            H.add_node(head, pos=word.upos, deprel=word.deprel)
            H.add_node(tail, pos=sent.words[word.head-1].upos, deprel=sent.words[word.head-1].deprel)
            H.add_edge(head, tail, weight=1)
bmt.plot_network(H)
# Let's examine if 'actin', 'talin', and 'vinculin' exist as distinct nodes
entities = ['actin','talin','vinculin']
nodes = H.nodes()
atoms = []
for node in nodes:
    for entity in entities:
        if entity in node:
            atoms.append(node)
atoms
%time
tuples = permutations(atoms, 2)
simple_path = []
for t in tuples:
    #print (t)
    paths = list(nx.all_simple_paths(H, source=t[0], target=t[1]))
    for p in paths:
        #print (p)
        simple_path.append(p)
for index, p in enumerate(simple_path):
    print (index, ':', p)<jupyter_output>0 : ['vinculin', 'has', 'negligible influence', 'internal actin filament dynamics']
1 : ['vinculin', 'has', 'negligible influence', 'contrasts', 'previous observations', 'with', 'talin', 'vinculin-binding protein', 'and', 'actin bending stiffness', 'internal actin filament dynamics']
2 : ['vinculin', 'has', 'negligible influence', 'contrasts', 'previous observations', 'talin', 'vinculin-binding protein', 'and', 'actin bending stiffness', 'internal actin filament dynamics']
3 : ['vinculin', 'has', 'negligible influence', 'internal actin filament dynamics', 'actin bending stiffness']
4 : ['vinculin', 'has', 'negligible influence', 'contrasts', 'previous observations', 'with', 'talin', 'vinculin-binding protein', 'and', 'actin bending stiffness']
5 : ['vinculin', 'has', 'negligible influence', 'contrasts', 'previous observations', 'talin', 'vinculin-binding protein', 'and', 'actin bending stiffness']
6 : ['vinculin', 'has', 'negligible influence', 'internal actin filament dynamics', 'acti[...]<jupyter_text>From above simple paths, they seems to make better sense among those entities of interest.  However, it still looks obscure on the 'direction' of the influence. Approach 3: Using stanza's tokenizer instead of providing my own pretokenized sentence as input to create a Directed Graph<jupyter_code>%time
I = nx.DiGraph() # using directed graph to compare with undirected Graph
for sent in doc.sentences:
    for word in sent.words:
        head = word.text
        tail = sent.words[word.head-1].text
        if word.upos == 'PUNCT' or sent.words[word.head-1].upos=='PUNCT':
            pass
        else:
            
            I.add_node(head, pos=word.upos, deprel=word.deprel)
            I.add_node(tail, pos=sent.words[word.head-1].upos, deprel=sent.words[word.head-1].deprel)
            I.add_edge(head, tail, weight=1)
bmt.plot_network(I)
entities = ['actin','talin','vinculin']
nodes = I.nodes()
atoms = []
for node in nodes:
    for entity in entities:
        if entity in node:
            atoms.append(node)
atoms
from itertools import permutations
tuples = permutations(atoms, 2)
for t in tuples:
    print (t)
%time
tuples = permutations(atoms, 2)
simple_path = []
for t in tuples:
    #print (t)
    paths = list(nx.all_simple_paths(I, source=t[0], target=t[1]))
    for p in paths:
        #print (p)
        simple_path.append(p)
for index, p in enumerate(simple_path):
    print (index, ':', p)<jupyter_output>0 : ['vinculin', 'binding', 'protein', 'talin']
1 : ['actin', 'protein', 'talin']<jupyter_text>From above simple paths, Directed Graph constructed from tokens parsed by default stanza processer simply erases any evidence that three entities are connected in any meaningful way Approach 4: Using my own pretokenized sentence as input to create a Directed Graph<jupyter_code>%time
J = nx.DiGraph() # using directed graph to compare with undirected Graph
for sent in doc_re.sentences:
    for word in sent.words:
        head = word.text
        tail = sent.words[word.head-1].text
        if word.upos == 'PUNCT' or sent.words[word.head-1].upos=='PUNCT':
            pass
        else:
            J.add_node(head, pos=word.upos, deprel=word.deprel)
            J.add_node(tail, pos=sent.words[word.head-1].upos, deprel=sent.words[word.head-1].deprel)
            J.add_edge(head, tail, weight=1)
bmt.plot_network(J)
entities = ['actin','talin','vinculin']
nodes = J.nodes()
atoms = []
for node in nodes:
    for entity in entities:
        if entity in node:
            atoms.append(node)
atoms
from itertools import permutations
tuples = permutations(atoms, 2)
for t in tuples:
    print (t)
%time
tuples = permutations(atoms, 2)
simple_path = []
for t in tuples:
    #print (t)
    paths = list(nx.all_simple_paths(J, source=t[0], target=t[1]))
    for p in paths:
        #print (p)
        simple_path.append(p)
for index, p in enumerate(simple_path):
    print (index, ':', p)<jupyter_output>0 : ['actin bending stiffness', 'internal actin filament dynamics']
1 : ['actin', 'talin']
2 : ['vinculin-binding protein', 'talin']
{'fraction_non_alphanumeric': 0.14922097871406628, 'fraction_numerical': 0.008448540706605223, 'mean_word_length': 4.089335566722501, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '538815', 'license': 'CC0-1.0', 'n_tokens_mistral': 3128, 'n_tokens_neox': 2975, 'n_words': 897, 'path': 'Day_2 -  Network Analysis on Sentence Dependency Tree.ipynb', 'repo_name': 'ghostintheshellarise/BiomedicalText', 'size': '9114'}