#python
#python
Вопрос:
У меня есть данные, хранящиеся в списке списков, организованных следующим образом:
lst = [
['FHControl', G, A]
['MNHDosed', G, C]
]
Для строки в lst: row[0] всего 12 категорий (я перечислил две в примере кода выше). Для строк [1] и строк [2] Меня интересуют только 6 комбинаций этих букв. Таким образом, у меня есть 72 возможных комбинации этих данных для каждой строки в lst, и мне нужно подсчитать экземпляры каждой комбинации без необходимости писать десятки вложенных циклов if.
Я пытаюсь создать две функции для анализа этих списков и сопоставления случаев этих 72 комбинаций. Как я могу использовать две функции, подобные тем, что я начинаю писать ниже, для обновления этих переменных? Нужно ли мне создавать словари как переменные класса, чтобы я мог продолжать обновлять их по мере выполнения обеих функций? Любое руководство было бы здорово!
Вот код, который у меня есть в настоящее время, который инициализирует все 72 переменные в 6 словарях (для 6 комбинаций букв в строке [1] и строке [2]):
def baseparser(lst):
TEMP = dict.fromkeys('FHDosed FHControl FNHDosed FNHControl '
'FTDosed FTControl MHDosed MHControl '
'MNHDosed MNHControl MTDosed MTControl'.split(), 0)
TRI_1, TRI_2, TRV_1, TRV_2, TRV_3, TRV_4 = ([dict(TEMP) for i in range(6)])
for row in lst:
if row[0] == 'FHDosed':
binner(row[0], row[1], row[2])
if row[0] == 'FHControl':
binner(row[0], row[1], row[2])
etc.
def binner(key, q, s):
if (q == 'G' and s == 'A') or (q =='C' and s =='T'):
TRI_1[key] = 1
elif (q == 'A' and s == 'G') or (q =='T' and s =='C'):
TRI_2[key] = 1
elif (q == 'G' and s == 'T') or (q =='C' and s =='A'):
TRV_1[key] = 1
elif (q == 'G' and s == 'C') or (q =='C' and s =='G'):
TRV_1[key] = 1
elif (q == 'A' and s == 'T') or (q =='T' and s =='A'):
TRV_1[key] = 1
elif (q == 'A' and s == 'C') or (q =='T' and s =='G'):
TRV_1[key] = 1
Комментарии:
1.
binner
Зависит от двух аргументов или трех? Если это зависит только отq
иs
которые выглядят как однобуквенные строки, то зачем вам нужны if-операторы, напримерif row[0] == 'FHDosed'
, перед вызовомbinner
, и не должно ли это бытьbinner(row[1], row[2])
вместоbinner(row[0], row[1], row[2])
?2. Ах, вы правы. Я не был ясен. Я чувствую, что binner должен зависеть от трех аргументов, первый из которых определяет, какой ключ в соответствующем словаре должен быть вызван для изменения значения ( =1 для каждого экземпляра). Вторые два аргумента используются циклом if, чтобы выбрать, с какого словаря выбирается начать. Я отредактирую свой код, чтобы отразить.
3. Кажется, нет никакой разницы между вызовом
binner
независимо от того, чтоrow[0]
равно. Есть ли детали, которые вы упускаете?4. @clintval будет ли для вас использование pandas ? Это было сделано именно для этих типов операций.
Ответ №1:
Ваш код может быть упрощен до:
TEMP = dict.fromkeys('''FHDosed FHControl FNHDosed FNHControl FTDosed FTControl MHDosed
MHControl MNHDosed MNHControl MTDosed MTControl'''.split(), 0)
TRI_1, TRI_2, TRV_1, TRV_2, TRV_3, TRV_4 = [TEMP.copy() for i in range(6)]
dmap = {
('G', 'A'): TRI_1,
('C', 'T'): TRI_1,
('A', 'G'): TRI_2,
('T', 'C'): TRI_2,
('G', 'C'): TRV_1,
('C', 'G'): TRV_1,
('A', 'T'): TRV_1,
('T', 'A'): TRV_1,
}
for row in lst:
key, q, s = row
dmap[q, s][key] = 1
Другая возможность — использовать один dict of dicts вместо 6 dicts:
TEMP = dict.fromkeys('''FHDosed FHControl FNHDosed FNHControl FTDosed FTControl MHDosed
MHControl MNHDosed MNHControl MTDosed MTControl'''.split(), 0)
TR = {key:TEMP.copy() for key in ('TRI_1', 'TRI_2', 'TRV_1', 'TRV_2', 'TRV_3', 'TRV_4')}
dmap = {
('G', 'A'): 'TRI_1',
('C', 'T'): 'TRI_1',
('A', 'G'): 'TRI_2',
('T', 'C'): 'TRI_2',
('G', 'C'): 'TRV_1',
('C', 'G'): 'TRV_1',
('A', 'T'): 'TRV_1',
('T', 'A'): 'TRV_1',
}
lst = [
['FHControl', 'G', 'A'],
['MNHDosed', 'G', 'C']
]
for row in lst:
key, q, s = row
TR[dmap[q, s]][key] = 1
print(TR)
Преимущество этого способа заключается в том, что у вас меньше dicts в вашем пространстве имен, и, возможно, будет проще реорганизовать код позже, используя dict of dicts вместо жестко закодированных 6 dicts.
Следуя предложению Полуночника, если у вас есть pandas, вы можете заменить dict of dicts на DataFrame . Тогда частота пар может быть вычислена с использованием pd.crosstabs следующим образом:
import pandas as pd
dmap = {
'GA': 'TRI_1',
'CT': 'TRI_1',
'AG': 'TRI_2',
'TC': 'TRI_2',
'GC': 'TRV_1',
'CG': 'TRV_1',
'AT': 'TRV_1',
'TA': 'TRV_1',
}
lst = [
['FHControl', 'G', 'A'],
['MNHDosed', 'G', 'C']
]
df = pd.DataFrame(lst, columns=['key', 'q', 's'])
df['tr'] = (df['q'] df['s']).map(dmap)
print(df)
# key q s tr
# 0 FHControl G A TRI_1
# 1 MNHDosed G C TRV_1
print(pd.crosstab(rows=[df['key']], cols=[df['tr']]))
дает
tr TRI_1 TRV_1
key
FHControl 1 0
MNHDosed 0 1
Комментарии:
1. Это сработало! Это упростит расширение скрипта в будущем. Я изучу Pandas … после небольшого чтения у меня может быть больше приложений, для которых Pandas идеально подойдут. Слава!