Pandas Dataframe: как разделить один столбец на несколько столбцов с горячим кодированием

У меня есть такой текстовый файл:

444537110                         3 11112111022002200022022111121222002...

Последнее поле во входном файле имеет длину 50 тыс. символов и может быть только 0,1 или 2. Мне нужна одна версия этого последнего поля с горячим кодированием. Итак, мой ожидаемый результат - это такой кадр данных:

id          chip   g1_0 g1_1 g1_2 g2_0 g2_1 g2_2 g3_0 g3_1 g3_2 g4_0 ... 
444537110   3      0    1    0    0    1    0    0    1    0    0

Я создал начальный кадр данных, прочитав входной файл:

df = pd.read_csv('test.txt', index_col=0, sep='\s+', header=None, names = ['chip', 'genos'])

Это создает кадр данных с 3 столбцами как:

id        chip  genos
444537110    3  1111211102200220000022022111121222000200022002...

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

[c for c in df['genos'].str]

но это не разделение символов

Я рассмотрел аналогичный вопрос и ответил здесь: Как я могу кодировать на Python?

но это касается только одного горячего кодирования и не связано с дополнительными сложностями разделения очень большого столбца.


person daragh    schedule 24.06.2019    source источник
comment
предполагаю, что вам может понадобиться df['genos'].str.get_dummies(), хотя не уверен в предоставленных данных   -  person anky    schedule 24.06.2019
comment
Только что попробовал это предложение, и оно не сработало. Он вернул фрейм данных с одним столбцом с genos в качестве заголовка столбца и только с одним значением.   -  person daragh    schedule 24.06.2019
comment
@daragh, не могли бы вы представить, что ваш последний столбец имеет длину всего 3 символа вместо 50 КБ, а затем опубликовать несколько многострочных образцов входных данных и полный желаемый вывод OHE? Это сделает ваши намерения более ясными. Потому что в его нынешнем виде кажется, что вы хотите OHE поле, которое может содержать 3 в степени 50 000 различных значений, что кажется плохой идеей (т.е. слишком много столбцов, чтобы быть полезным)   -  person Dan    schedule 24.06.2019
comment
@Dan Я передаю полученный кадр данных в нейронную сеть, поэтому я ожидаю 150 тыс. столбцов.   -  person daragh    schedule 24.06.2019


Ответы (3)


Сначала создайте DataFrame с преобразованием строки в список и вызовите get_dummies< /а>:

df1 = pd.DataFrame([list(x) for x in df['genos']], index=df.index).add_prefix('g')
df2 = pd.get_dummies(df1)

Если необходимо добавить новый столбец в исходный (если возможно, отсутствует какая-либо комбинация), используйте DataFrame.reindex по разделенным столбцам с _ и по всей комбинации, созданной MultiIndex.from_product:

df1 = pd.DataFrame([list(x) for x in df.pop('genos')], index=df.index).add_prefix('g')
df2 = pd.get_dummies(df1)

splitted = df2.columns.str.split('_')
df2.columns = [splitted.str[0].astype(int) + 1, splitted.str[1].astype(int)]
#
mux = pd.MultiIndex.from_product([df2.columns.get_level_values(0), [0,1,2]])
df2 = df2.reindex(mux, axis=1, fill_value=0)
df2.columns = [f'g{a}_{b}' for a, b in df2.columns]
print (df2)
   g1_0  g1_1  g1_2  g2_0  g2_1  g2_2  g3_0  g3_1  g3_2  g4_0  ...  g32_2  \
0     0     1     0     0     1     0     0     1     0     0  ...      1   

   g33_0  g33_1  g33_2  g34_0  g34_1  g34_2  g35_0  g35_1  g35_2  
0      1      0      0      1      0      0      0      0      1  

[1 rows x 105 columns]
person jezrael    schedule 24.06.2019
comment
Вы уверены, что это дает правильный результат? Первый ген genos должен быть закодирован как g1_X (X в зависимости от его значения) - person Sebastien D; 24.06.2019
comment
@SebastienD - извините, было время ужина, поэтому ответ был отредактирован. - person jezrael; 24.06.2019

Имея в виду комментарий @Dan к вашему вопросу о том, что вы закончите столбцами 50k * 3, вы можете получить желаемый результат, сделав это:

string ="444537110 3 11112111022002200022022111121222002"
df = pd.DataFrame([string.split(" ")],columns=['id','chip','genos'])
max_number_of_genes = int(df.genos.apply(lambda x : len([y for y in x])).max())

#Create columns 
for gene in range(1,max_number_of_genes+1):
    for y in range(4):
        df['g{}_{}'.format(gene, y)] = 0

#Iterating over genos values 
for row_number, row in df.iterrows():
    genos = [int(x) for x in df.iloc[row_number, 2]]
    for gene_number, gene in enumerate(genos):     
        df.loc[row_number, 'g{}_{}'.format(gene_number+1, gene)] = 1 

print(df)

Вывод

+----+------------+-------+--------------------------------------+-------+-------+-------+-------+-------+-------+-------+------+--------+--------+--------+--------+--------+--------+--------+--------+--------+-------+
|    |    id      | chip  |                genos                 | g1_0  | g1_1  | g1_2  | g1_3  | g2_0  | g2_1  | g2_2  | ...  | g33_2  | g33_3  | g34_0  | g34_1  | g34_2  | g34_3  | g35_0  | g35_1  | g35_2  | g35_3 |
+----+------------+-------+--------------------------------------+-------+-------+-------+-------+-------+-------+-------+------+--------+--------+--------+--------+--------+--------+--------+--------+--------+-------+
| 0  | 444537110  |    3  | 11112111022002200022022111121222002  |    0  |    1  |    0  |    0  |    0  |    1  |    0  | ...  |     0  |     0  |     1  |     0  |     0  |     0  |     0  |     0  |     1  |     0 |
+----+------------+-------+--------------------------------------+-------+-------+-------+-------+-------+-------+-------+------+--------+--------+--------+--------+--------+--------+--------+--------+--------+-------+
person Sebastien D    schedule 24.06.2019

Если вы разделяете только 50 тыс. символов, вы можете использовать необработанный Python (для удобочитаемости):

>>> a,b,c = zip(*[{0:(1,0,0),1:(0,1,0),2:(0,0,1)}[int(c)] for c in df['genos'][0]])
>>> a,b,c
((0, 0, 0, 0, 0, 0, ...), (1, 1, 1, 1, 0, 1, ...), (0, 0, 0, 0, 1, 0, ...))
person Jonas Byström    schedule 24.06.2019