Заполнение столбцов данных в зависимости от условия в pandas - PullRequest
0 голосов
/ 19 апреля 2020

У меня есть два кадра данных, как показано ниже

         df_input                                    df_output
id       POLL_X  POLL_Y  POLL_Z ..     id   Pass_01  Pass_02  Pass_03 .....
110101       1       2       4       110101             
110102       2       1       3       110102

, и требуется заполнить df_ouput на основе значения в df_input

            df_input                                    df_output
  id   POLL_X  POLL_Y  POLL_Z ....       id   Pass_01  Pass_02  Pass_03 .....
110101     1       2       3            110101     X       Y         Z  
110102     2       1       3            110102     Y       X         Z

, поэтому в основном это значение столбца из df_input будет значением ячейки в df_output при совпадении и на основе df_input.id == df_output.id

Я пытался, как показано ниже

def function1(df_input, number):
       dfwithCols = df_input[df_input.columns[pd.Series(df_input.columns).str.startswith('POLL_')]]
       list_cols = dfwithCols .columns[(dfwithCols == float(number)).iloc[0]]
       colValue = (dfReduced == float(index)).idxmax(axis=1)[0]
       return colValue

- функция драйвера ---

for i in range(1,number_of_columnswithPass):
      df_output['Pass_'+i] = function1(df_input,i)

number_of_columnsiwthPass является константой, которая дает общее количество столбцов с именем в качестве прохода.

Также я не могу перебрать каждую строку, так как это займет огромное время, приходится делать это на основе столбцов или лямбда-выражений на основе

есть другие столбцы в обоих фреймах данных, также df_input.id == df_output.id должен соответствовать

Общее количество столбцов может быть около 40, некоторые тестовые значения включают POLL_DNW, POLL_DO, POLL_DOES, POLL_SIG: 2
, поэтому я должен взять все, что есть после '_' и номеров столбцов, таких как 01,02,03,04 ---- 10,11, - 21, --- 39,40

Ответы [ 3 ]

1 голос
/ 19 апреля 2020

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

Для выполнения вашей задачи:

  1. import re (будет использоваться в данный момент).

  2. Определите следующую функцию, генерирующую строку вывода, на основе строки источника:

    def genRow(row):
        ind = []
        vals = []
        for k, v in row.iteritems():
            mtch = re.match('POLL_(.+)', k)
            if mtch:
                ind.append('Pass_' + str(v).zfill(2))
                vals.append(mtch.group(1))
            else:
                ind.append(k)
                vals.append(v)
        return pd.Series(vals, index=ind).rename(row.name)
    

    Обратите внимание, что эта функция "заменяет" любой столбец POLL _... на соответствующий столбец Pass _... и оставляет другие столбцы такими, какими они были.

  3. Применить это:

    df_output = df_input.apply(genRow, axis=1).reindex(columns=df_output.columns)
    

Шаги:

  • df_input.apply(...) - Создать «предварительный» выходной DataFrame. Обратите внимание, что на данный момент порядок столбцов в алфавитном порядке .
  • reindex(...) - переиндексировать указанный выше DataFrame, используя имена столбцов из df_output , обеспечивая правильный порядок столбцов.
  • df_output = - перезаписать df_output с указанным выше результатом.

Редактировать

Если ваш входной DataFrame содержит повторяющиеся значения в POLL_ ... столбцы, небольшая модификация необходима. Такой случай приводит к выходной строке, в которой эти два (или более) элемента имеют одинаковый индекс , поэтому весь DataFrame не может быть создан, если он содержит такую ​​строку.

Средство устранения «сжать» такие элементы в один элемент с исходным индексом и всеми значениями, преобразованными в строку, содержащую, например, список исходных значений через запятую.

Для этого измените последнюю строку в genRow функция для:

out = pd.Series(vals, index=ind).rename(row.name)
return out.groupby(lambda key: key).apply(lambda lst: (', '.join(sorted(lst))))
0 голосов
/ 19 апреля 2020

Вы можете сделать это с stack, unstack и некоторыми set_index и reset_index для воспроизведения с какими столбцами в логи c.

df2 = (df1.set_index('id') #set index any columns not in the logic of pass
          # remove the prefix Pass_
          .rename(columns=lambda col: col.replace('Pass_', ''))
          # stack the datafrae to make it a serie and sort the passes
          .stack().sort_values()
          # next two method exchange the old pass index to the new pass index
          .reset_index(level=1, name='Pass')
          .set_index('Pass', append=True)['level_1']
          # from the serie to the dataframe shape
          .unstack()
          # rename the columns with the prefix pass_
          .rename(columns=lambda col: f'Pass_{col:02}')
          # rename axis to none
          .rename_axis(None, axis=1)
          # but back the id as a column
          .reset_index())

print (df2)
       id Pass_01 Pass_02 Pass_03
0  110101       X       Y       Z
1  110102       Y       X       Z

Примечание: если у вас есть другие столбцы, которые вы не хотите включать в процесс, то сначала установите их в качестве индекса с идентификатором, например set_index(['id','col1', ...])

0 голосов
/ 19 апреля 2020

создайте два фрейма данных из df_input и output, объедините их и поверните обратно, чтобы получить окончательный фрейм данных:

#create first dataframe
    res1 = pd.wide_to_long(df,
                       stubnames='Pass',
                       i='id',
                       j='letters',
                       sep='_',
                       suffix='[A-Z]').reset_index()
                          )
res1

     id letters Pass
0   110101  X   1
1   110102  X   2
2   110101  Y   2
3   110102  Y   1
4   110101  Z   4
5   110102  Z   3

#create second dataframe
res2 = (df1
        .melt('id')
        .drop('value', axis=1)
        .assign(numbers=lambda x: x.variable.str.split('_').str.get(-1))
        .astype( {'numbers': int})
       )

res2

      id    variable    numbers
0   110101  Pass_01       1
1   110102  Pass_01       1
2   110101  Pass_02       2
3   110102  Pass_02       2
4   110101  Pass_03       3
5   110102  Pass_03       3

#merge the two dataframes, and pivot to get ur final output

outcome = (res1
           .merge(res2,
                  left_on=['id','Pass'],
                  right_on=['id','numbers'],
                  how='right')
           .pivot(columns='variable',values='letters',index='id')
           .bfill()
           .reset_index()
           .rename_axis(columns=None)
          )

outcome

      id    Pass_01 Pass_02 Pass_03
0   110101     X       Y       Z
1   110102     Y       X       Z
...