Obróbka tekstu w pythonie - trudne zadanie

0

Mam taki plik tekstowy (wklejam tylko część):
DOPC 8x8 DPPC 2012apr_mp2_Testing
35325
1DOPC H6 10 5.888 5.839 1.884
1DOPC H7 11 5.950 5.878 1.726
1DOPC H8 12 5.928 6.005 1.845
1DOPC C4 13 5.756 6.033 1.643
1DOPC H9 14 5.659 6.053 1.598
1DOPC H10 15 5.821 5.993 1.565
1DOPC H11 16 5.799 6.129 1.674
1DOPC C5 17 5.548 6.092 1.837
1DOPC H12 18 5.497 6.120 1.929
1DOPC H13 19 5.586 6.185 1.794
1DOPC P1 20 5.332 5.949 1.795
1DOPC O1 21 5.386 5.819 1.842
129SOL OW17665 0.230 0.628 0.113
129SOL HW117666 0.137 0.626 0.150
129SOL HW217667 0.231 0.589 0.021
130SOL OW17668 0.225 0.275 0.996
130SOL HW117669 0.260 0.258 1.088
130SOL HW217670 0.137 0.230 0.984
131SOL OW17671 0.019 0.368 0.647
131SOL HW117672 -0.063 0.411 0.686
131SOL HW217673 -0.009 0.295 0.584
132SOL OW17674 0.569 1.275 1.165
132SOL HW117675 0.476 1.268 1.128
132SOL HW217676 0.580 1.364 1.209
133SOL OW17677 1.555 1.511 0.703
133SOL HW117678 1.498 1.495 0.784
133SOL HW217679 1.496 1.521 0.623
134SOL OW17680 1.135 0.703 0.717
134SOL HW117681 1.192 0.781 0.692
134SOL HW217682 1.075 0.729 0.793
135SOL OW17683 1.755 0.607 0.231
135SOL HW117684 1.743 0.594 0.132
135SOL HW217685 1.725 0.526 0.280

Muszę usunąć wszystkie cząsteczki wody z tego pliku tekstowego(SOL), którego koordynaty w osi Z (koordynaty osi Z to ostatnia liczba w każdej linijce) znajdują się załóżmy pomiędzy 0.7, a 1.1), czyli np. jeśli jakiś atom znajduje się np. " 134SOL OW17680 1.135 0.703 ** 0.717**" to usuwam :
134SOL OW17680 1.135 0.703 0.717
134SOL HW117681 1.192 0.781 0.692
134SOL HW217682 1.075 0.729 0.793
całą cząsteczkę, czyli 3 atomy (bo wystarczy, że choćby jeden atom się znajdzie pomiędzy 0.7 a 1.1

Na razie wiem jak usunąć daną linijkę, jeśli występuje jakieś tam słowo:

bad_words = ['HW', 'OW', "4.460"]

with open('system_solvate.gro', "r") as oldfile, open('clear.gro', 'w') as newfile:
    for line in oldfile:
        if not any(bad_word in line for bad_word in bad_words):
            newfile.write(line)

Ale jak zrobić tak, żeby usunąć wszystkie linijki z liczbą w koordynacie z np. od 0.7 do 1.1 i jednocześnie inne atomy przynależące do tej cząsteczki? Nie mam zupełnie pomysłu. Wiem, że dodatkowo trzeba uwzględnić to, żeby skrypt sprawdzał te liczby na miejscu od 40 do 45 w każdej linijce (bo tam są te koordynaty z, bo są też liczby z koordynatami x i y). Błagam podpowiedzcie chociaż trochę.

Gość napisał skrypt w perlu robiący to, ale kompletnie nie znam tego języka http://www.mdtutorials.com/gmx/membrane_protein/03_solvate.html
www.mdtutorials.com/gmx/membrane_protein/Files/water_deletor.pl

2
input = "DOPC 8x8 DPPC 2012apr_mp2_Testing\r\n35325\r\n1DOPC H6 10 5.888 5.839 1.884\r\n1DOPC H7 11 5.950 5.878 1.726\r\n1DOPC H8 12 5.928 6.005 1.845\r\n1DOPC C4 13 5.756 6.033 1.643\r\n1DOPC H9 14 5.659 6.053 1.598\r\n1DOPC H10 15 5.821 5.993 1.565\r\n1DOPC H11 16 5.799 6.129 1.674\r\n1DOPC C5 17 5.548 6.092 1.837\r\n1DOPC H12 18 5.497 6.120 1.929\r\n1DOPC H13 19 5.586 6.185 1.794\r\n1DOPC P1 20 5.332 5.949 1.795\r\n1DOPC O1 21 5.386 5.819 1.842\r\n129SOL OW17665 0.230 0.628 0.113\r\n129SOL HW117666 0.137 0.626 0.150\r\n129SOL HW217667 0.231 0.589 0.021\r\n130SOL OW17668 0.225 0.275 0.996\r\n130SOL HW117669 0.260 0.258 1.088\r\n130SOL HW217670 0.137 0.230 0.984\r\n131SOL OW17671 0.019 0.368 0.647\r\n131SOL HW117672 -0.063 0.411 0.686\r\n131SOL HW217673 -0.009 0.295 0.584\r\n132SOL OW17674 0.569 1.275 1.165\r\n132SOL HW117675 0.476 1.268 1.128\r\n132SOL HW217676 0.580 1.364 1.209\r\n133SOL OW17677 1.555 1.511 0.703\r\n133SOL HW117678 1.498 1.495 0.784\r\n133SOL HW217679 1.496 1.521 0.623\r\n134SOL OW17680 1.135 0.703 0.717\r\n134SOL HW117681 1.192 0.781 0.692\r\n134SOL HW217682 1.075 0.729 0.793\r\n135SOL OW17683 1.755 0.607 0.231\r\n135SOL HW117684 1.743 0.594 0.132\r\n135SOL HW217685 1.725 0.526 0.280"

input2 = "DOPC 8x8 DPPC 2012apr_mp2_Testing\r\n35325\r\n1DOPC H6 10 5.888 5.839 1.884\r\n1DOPC H7 11 5.950 5.878 1.726\r\n1DOPC O8 12 5.928 6.005 1.845\r\n1DOPC H8 12 5.928 6.005 1.845\r"


import numpy as np
#from example_data import SQLresult1 as input
#from example_data import SQLresult2 as input2

#print(input)

interest = ['H', 'H', 'O']
H2O_paricle = set(interest) == set(['H', 'O']) and len(interest) == 3
print(H2O_paricle)

input_lines = input.split('\n')
input_lines = input_lines[2:]
print(f'input_lines.len: {len(input_lines)}')
print(input_lines)

def line_has_particle(symbol, line):
    return (' ' + symbol) in line

def line_has_Z_in_range(min, max, line):
    return min < float(line.split(" ")[-1]) < max

def is_H2O_paricle(particle_seq):
    return set(particle_seq) == set(['H', 'O']) and len(particle_seq) == 3
#print(input_lines)

print(input_lines[0])
result = line_has_particle('H', input_lines[0])
print(result)

""" Tutaj zaczyna się rozwiązanie """
indexes_to_remove = []
temp_storage = []

for (i, line) in enumerate(input_lines):
    if is_H2O_paricle(temp_storage):
        indexes_to_remove.append(i-3-1)
        print(f'temp_storage: {temp_storage}')
        temp_storage = []
    elif len(temp_storage) > 2: temp_storage = []

    if line_has_particle('O', line) and line_has_Z_in_range(0.7, 1.1, line):
        temp_storage.append('O')
        continue
    elif line_has_particle('H', line) and line_has_Z_in_range(0.7, 1.1, line):
        temp_storage.append('H')
        continue
if is_H2O_paricle(temp_storage):
        indexes_to_remove.append(i-3-1)

print(f'temp_storage: {temp_storage}')
print(f'indexes_to_remove: {indexes_to_remove}')
for i in indexes_to_remove:
    for j in range(3):
        print(input_lines[i+j])
    print("")

print("##################")

for i in indexes_to_remove:
    for j in range(3):
       input_lines.pop(i+j)

for i in input_lines:
    print(i)

# def filter_indexies(indexes):
#     coupled = zip(*[indexes[i::2] for i in range(2)])

#     indexes_to_remove = [indexes[0]]

#     for (first, second) in coupled:
#         if second - first > 1: indexes_to_remove.append(second)
    
#     return indexes_to_remove

# print(f'real_indexes_to_remove: {filter_indexies(indexes_to_remove)}')


1

Zrobiłem taką wersję:

class Atom:
    def __init__(self, line):
        splitted = line.split()
        try:
            self.coord1 = splitted[0]
            self.coord2 = splitted[1]
            self.x = float(splitted[2])
            self.y = float(splitted[3])
            self.z = float(splitted[4])
            self.line = line
        except IndexError:
            raise ValueError
            
    def is_z_invalid(self, range_min, range_max):
        if range_min <= self.z <= range_max:
            return True
        else:
            return False
            
remove_lookup = set()
output = []
input = []

with open('input.txt', 'r') as f:
    input = f.readlines()
    
for line in input:
    try:
        atom = Atom(line)
        if atom.coord1 in remove_lookup:
            continue
        if atom.is_z_invalid(0.7, 1.1):
            remove_lookup.add(atom.coord1)
    except ValueError:
        continue
        
for line in input:
    try:
        coord1 = line.split(' ', 1)[0]
        if coord1 not in remove_lookup:
            output.append(line)
    except IndexError:
        output.append(line)
            
with open('output.txt', 'w') as f:
    f.writelines(line for line in output)

Przechodzimy przez linie z pliku dwa razy. Raz aby stworzyć "look up table" - zapisujemy sobie, jakie cząsteczki chcemy odrzucić. Przy drugiej iteracji wszystkie te cząsteczki/atomy (nie znam nazewnictwa), które mają wspólny element z tymi zapisanymi w look up table. Preferuję jak najkrócej "trzymać" handle do pliku więc otwieram i zapisuje pliki osobno z listy.

0

Wielkie dzięki, jesteście geniuszami, no to spróbuję go uruchomić i trochę rozpracować, bo w obróbce tekstu jeśli chodzi o pythona to wcześniej nie siedziałem i nie jestem w tym zbyt dobry.

1

Python się do tego nadaje, ale są lepsze narzędzia do analizy plików tekstowych, jak choćby awk. Z Twojego podanego pliku usunąłem dwie pierwsze linie, bo widać, że to jest jakiś nagłówek. Resztę przepuściłem przez awk, gdzie wykorzystałem tablice asocjacyjne:

awk 'BEGIN {while (getline < "file") {if ($1 ~ /SOL/ && $NF > 0.7 && $NF < 1.1) name[$1]=$1}} {if ($1 != name[$1]) print}' file

Na wyjściu otrzymałem to:

1DOPC H6 10 5.888 5.839 1.884
1DOPC H7 11 5.950 5.878 1.726
1DOPC H8 12 5.928 6.005 1.845
1DOPC C4 13 5.756 6.033 1.643
1DOPC H9 14 5.659 6.053 1.598
1DOPC H10 15 5.821 5.993 1.565
1DOPC H11 16 5.799 6.129 1.674
1DOPC C5 17 5.548 6.092 1.837
1DOPC H12 18 5.497 6.120 1.929
1DOPC H13 19 5.586 6.185 1.794
1DOPC P1 20 5.332 5.949 1.795
1DOPC O1 21 5.386 5.819 1.842
129SOL OW17665 0.230 0.628 0.113
129SOL HW117666 0.137 0.626 0.150
129SOL HW217667 0.231 0.589 0.021
131SOL OW17671 0.019 0.368 0.647
131SOL HW117672 -0.063 0.411 0.686
131SOL HW217673 -0.009 0.295 0.584
132SOL OW17674 0.569 1.275 1.165
132SOL HW117675 0.476 1.268 1.128
132SOL HW217676 0.580 1.364 1.209
135SOL OW17683 1.755 0.607 0.231
135SOL HW117684 1.743 0.594 0.132
135SOL HW217685 1.725 0.526 0.280

O to Ci chodziło?

1 użytkowników online, w tym zalogowanych: 0, gości: 1