Как выполнить вычисление для каждой строки в DF, ссылаясь на данные, присутствующие в другой строке? - PullRequest
0 голосов
/ 22 января 2019

Я ищу эффективный способ вычисления новых данных столбца (для каждой строки), используя информацию, которая может присутствовать в какой-то другой строке.

Вот примерное подмножество:

df = pd.DataFrame({'baseSeq': {0: 'ADTPICR', 1: 'ADTPICR', 2: 'AVALFAED', 3: 'AVALFAED', 4: 'AVALFAED', 5: 'AVALFAED', 6: 'AVALFAED', 7: 'AVALFAED'}, 'modSeq': {0: 'ADT[+16]PICR', 1: 'ADTPICR', 2: 'AVALFAED[+16]', 3: 'AVALFAE[+16]D', 4: 'AVALFAED', 5: 'AVALFAED[-30]', 6: 'AVALFAED', 7: 'AVALFAED'}, 'charge': {0: 2, 1: 2, 2: 2, 3: 2, 4: 2, 5: 3, 6: 3, 7: 4}, 'modType': {0: 'hydoxy', 1: 'UNMOD', 2: 'hydroxy', 3: 'hydroxy', 4: 'UNMOD', 5: 'decarbox', 6: 'UNMOD', 7: 'UNMOD'}, 'area_0': {0: 1862, 1: 22737, 2: 40060, 3: 40131, 4: 21962, 5: 12, 6: 21885, 7: 2116}, 'area_25': {0: 2472, 1: 30966, 2: 2423, 3: 2407, 4: 34387, 5: 16, 6: 35444, 7: 3072}, 'area_50': {0: 3015, 1: 24660, 2: 3553, 3: 3577, 4: 29860, 5: 40, 6: 33511, 7: 2974}})
     baseSeq          modSeq  charge    modType  area_0  area_25  area_50
0    ADTPICR    ADT[+16]PICR       2     hydoxy    1862     2472     3015
1    ADTPICR         ADTPICR       2      UNMOD   22737    30966    24660
2   AVALFAED   AVALFAED[+16]       2    hydroxy   40060     2423     3553
3   AVALFAED   AVALFAE[+16]D       2    hydroxy   40131     2407     3577
4   AVALFAED        AVALFAED       2      UNMOD   21962    34387    29860
5   AVALFAED   AVALFAED[-30]       3   decarbox      12       16       40
6   AVALFAED        AVALFAED       3      UNMOD   21885    35444    33511
7   AVALFAED        AVALFAED       4      UNMOD    2116     3072     2974

В частности, я хочу вычислить новые столбцы для «немодифицированной дроби» каждой модифицированной последовательности (по существу, каждой строки), а затем расширить ее на несколько столбцов «области».

fracUnmod = (1- (area_modified / (area_modified + area_unmodified)))

Значения "area_unmodified" должны поступать из разных строк, поэтому я не могу придумать, как это сделать, используя такую ​​функцию, как df.concat() или df.insert(). Правильные значения "area_unmodified" определяются из строки, которая разделяет те же значения 'baseSeq' и 'charge', но без каких-либо изменений (для удобства я включил столбец 'modType').

Мне не нужно вычислять fracUnmod для неизмененных строк, но я не хочу удалять эти строки из полученного результата (оставляя их просто, получаем fracUnmod = 0.5, что нормально).

Иногда встречаются строки, в которых не найдено неизмененных версий (не показаны), но я их пропустил, так как мне известно, что я задаю второстепенные вопросы в отдельном сообщении, плюс я смог отфильтровать их вручную в Excel.

Я делал что-то вроде этого, проходя по каждой строке:

for X in df.columns[df.columns.str.contains('area')].tolist():
    df[X.replace('area', 'fracUnmod')] = ''
for row in df.index:
    for X in df.columns[df.columns.str.contains('area')].tolist():
        Y = X.replace('area', 'fracUnmod')
        df[Y].iloc[row] = (1 - ((df.iloc[row][X]) / ((df.iloc[row][X]) + (df[(df.baseSeq==df.iloc[row].baseSeq) & (df.charge==df.iloc[row].charge) & (df.baseSeq==df.modSeq)][X].item()))))

Это работает, но это занимает много времени (~ 10 минут) для моего полного набора данных (650 строк для "строки" для цикла, 10 столбцов "области" для "X" для цикла).

Я также поднимаю этот надоедливый «SettingWithCopyWarning» - можно ли этого избежать, добавив .ascopy() к «Цепочечному назначению» моей последней строки? Я никогда не мог понять это.

Я несколько раз читал, что по возможности следует избегать циклов, поэтому я предполагаю, что это проблема. Есть ли более чистый и быстрый способ сделать это?

Каждая комбинация 'modSeq' и 'charge' уникальна, поэтому, возможно, я захочу сделать что-нибудь с df.groupby() или df.merge() и использовать временный DF?

Я часто сталкиваюсь с этой ситуацией, поэтому я стремлюсь узнать, как я могу делать такие вещи без использования петель (или, по крайней мере, увеличения скорости).

1 Ответ

0 голосов
/ 23 января 2019

Если мы создадим несколько временных столбцов, можно векторизовать вашу математическую операцию, и это должно резко сократить количество времени.После этого мы можем удалить временные столбцы.

Используя приведенный вами пример, он выглядит следующим образом:

  1. Мы начинаем с df, который выглядит следующим образом:
    baseSeq    modSeq        charge     modType   area_0  area_25   area_50
0   ADTPICR    ADT[+16]PICR       2     hydoxy      1862     2472      3015
1   ADTPICR    ADTPICR            2     UNMOD      22737    30966     24660
2   AVALFAED   AVALFAED[+16]      2     hydroxy    40060     2423      3553
3   AVALFAED   AVALFAE[+16]D      2     hydroxy    40131     2407      3577
4   AVALFAED   AVALFAED           2     UNMOD      21962    34387     29860
5   AVALFAED   AVALFAED[-30]      3     decarbox      12       16        40
6   AVALFAED   AVALFAED           3     UNMOD      21885    35444     33511
7   AVALFAED   AVALFAED           4     UNMOD       2116     3072      2974

Создайте список всех столбцов 'area':

area_cols = df.columns[df.columns.str.contains('area')].tolist()

Следующие 5 строк кода создают временную df, содержащую немодированные областей для каждого baseSeq / charge комбо:

temp_df = df[['baseSeq'] + ['charge'] + ['modType'] + area_cols].groupby(['baseSeq', 'charge', 'modType'], axis=0).sum()
temp_df = temp_df.reset_index(level=2)
temp_df = temp_df[temp_df['modType'] == 'UNMOD']
temp_df = temp_df.drop('modType', axis=1)
temp_df.rename(columns = lambda x: 'unmod_' + x, inplace=True)

Вот как выглядит временный df на этом этапе:

                unmod_area_0    unmod_area_25   unmod_area_50
baseSeq  charge         
ADTPICR       2        22737            30966          24660
AVALFAED      2        21962            34387          29860
              3        21885            35444          33511
              4         2116             3072           2974

Затем мы присоединяем этот временный фрейм данных к нашему основному фрейму данных, чтобы в столбцах отображалось правильное количество немодированных областей для каждого baseSeq / charge комбо:

df = df.join(temp_df, on=['baseSeq', 'charge'])

На этом этапе наш массив данных выглядит следующим образом:

    baseSeq     modSeq       charge  modType    area_0  area_25 area_50 unmod_area_0    unmod_area_25   unmod_area_50
0   ADTPICR     ADT[+16]PICR      2  hydoxy       1862     2472    3015        22737            30966           24660
1   ADTPICR     ADTPICR           2  UNMOD       22737    30966   24660        22737            30966           24660
2   AVALFAED    AVALFAED[+16]     2  hydroxy     40060     2423    3553        21962            34387           29860
3   AVALFAED    AVALFAE[+16]D     2  hydroxy     40131     2407    3577        21962            34387           29860
4   AVALFAED    AVALFAED          2  UNMOD       21962    34387   29860        21962            34387           29860
5   AVALFAED    AVALFAED[-30]     3  decarbox       12       16      40        21885            35444           33511
6   AVALFAED    AVALFAED          3  UNMOD       21885    35444   33511        21885            35444           33511
7   AVALFAED    AVALFAED          4  UNMOD        2116     3072    2974         2116             3072            2974
Теперь по основной части: мы перебираем каждый столбец 'area' и вычисляем желаемую долю.Это вычисление векторизация по каждой строке столбца и должно значительно ускорить процесс.
for col in area_cols:
    num = col.split('_')[1]
    df['fracUnmod_' + num] = 1 - (df[col] / (df[col] + df['unmod_' + col]))

Чтобы очистить вещи, давайте закончим, удалив временные столбцы, которые отображают немодированные области, и удалим наш временный фрейм данных.

df = df.drop(['unmod_' + c for c in area_cols], axis=1)

del(temp_df)

Конечный кадр данных выглядит следующим образом:

    baseSeq     modSeq       charge  modType    area_0  area_25 area_50 fracUnmod_0  fracUnmod_25   fracUnmod_50
0   ADTPICR     ADT[+16]PICR      2  hydoxy       1862     2472    3015    0.924306      0.926072       0.891057
1   ADTPICR     ADTPICR           2  UNMOD       22737    30966   24660    0.500000      0.500000       0.500000
2   AVALFAED    AVALFAED[+16]     2  hydroxy     40060     2423    3553    0.354100      0.934175       0.893664
3   AVALFAED    AVALFAE[+16]D     2  hydroxy     40131     2407    3577    0.353695      0.934582       0.893023
4   AVALFAED    AVALFAED          2  UNMOD       21962    34387   29860    0.500000      0.500000       0.500000
5   AVALFAED    AVALFAED[-30]     3  decarbox       12       16      40    0.999452      0.999549       0.998808
6   AVALFAED    AVALFAED          3  UNMOD       21885    35444   33511    0.500000      0.500000       0.500000
7   AVALFAED    AVALFAED          4  UNMOD        2116     3072    2974    0.500000      0.500000       0.500000

Это тот же вывод, что и ваш исходный вложенный цикл for.Но, надеюсь, будет намного быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...