Панды преобразуют логический столбец в имя столбца, когда истина - PullRequest
1 голос
/ 14 октября 2019

У меня есть df с логическими значениями (ну, например, int значения 0 или 1, но это сейчас не важно):

A B C D
0 0 1 0
1 0 0 0
0 1 1 1
1 0 0 1

И я хочу преобразовать его так, чтобы "1" (True) значения преобразуются в имя заголовка столбца, а значения 0 в NaN. Полученный df не должен иметь заголовок. Ожидаемый результат:

NaN NaN C   NaN
A   NaN NaN NaN
NaN B   C   D
A   NaN NaN D

Итерации по строкам и присвоение этих значений с помощью проверки могут работать, но нет ли более быстрого / более пандас-идиоматического способа?

Ответы [ 6 ]

2 голосов
/ 14 октября 2019

Может быть что-то с DataFrame.apply :

df.apply(lambda s: [s.name if v == 1 else np.nan for v in s])
2 голосов
/ 14 октября 2019

С numpy где

np.where(df == 1, df.columns, np.nan)

array([[nan, nan, 'C', nan],
       ['A', nan, nan, nan],
       [nan, 'B', 'C', 'D'],
       ['A', nan, nan, 'D']], dtype=object)

Как преобразовать np.array в pd.DataFrame (добавлено @jezrael)

df = pd.DataFrame(np.where(df == 1, df.columns, np.nan), columns=df.columns)
print (df)
     A    B    C    D
0  NaN  NaN    C  NaN
1    A  NaN  NaN  NaN
2  NaN    B    C    D
3    A  NaN  NaN    D
1 голос
/ 14 октября 2019

Используйте numpy.where с конструктором DataFrame и параметром без столбцов, если производительность важна:

df = pd.DataFrame(np.where(df == 1, df.columns, np.nan))
print (df)
     0    1    2    3
0  NaN  NaN    C  NaN
1    A  NaN  NaN  NaN
2  NaN    B    C    D
3    A  NaN  NaN    D

И при необходимости выведите файл без столбцов и значений индекса, добавьте index=False и header=None до DataFrame.to_csv:

df.to_csv('file.csv', index=False, header=None)

РЕДАКТИРОВАТЬ:

Если важна производительность, вы можете избежать apply, потому что петли под капотом,Здесь для наиболее векторизованного и быстрого решения лучше всего использовать np.where:

#[40000 rows x 40 columns]
df = pd.concat([df] * 10000, ignore_index=True)
df = pd.concat([df] * 10, ignore_index=True, axis=1)


In [180]: %%timeit
     ...: for i in df.columns:
     ...:     df[i] = df[i].apply(lambda x: i if x==1 else np.nan)
     ...:     
690 ms ± 13.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [181]: %%timeit
     ...: df.apply(lambda s: [s.name if v == 1 else np.nan for v in s])
     ...: 
680 ms ± 23 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [182]: %%timeit
     ...: pd.DataFrame(np.where(df == 1, df.columns, np.nan))
     ...: 
42.7 ms ± 3.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [183]: %%timeit
     ...: df.T.where(df.T != 1, df.columns).T.where(df != 0, np.nan)
     ...: 
17 s ± 644 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1 голос
/ 14 октября 2019

вы можете использовать np.where или pd.mask, как показано ниже

np.where(df.values==1, df.columns, np.nan)

## or
df.mask(df==1,df.columns)
1 голос
/ 14 октября 2019

Вы можете использовать это:

for i in df.columns:
    df[i] = df[i].apply(lambda x: i if x==1 else np.nan)
df.columns = [''] * len(df.columns)
0 голосов
/ 14 октября 2019

Вы также можете использовать where из pandas:

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.where.html)

Обратите внимание, что T важно иметь правильный результат.

import pandas as pd
import numpy as np


df = pd.DataFrame({'A': [0,1,0,1],
                   'B': [0,0,1,0],
                   'C': [1,0,1,0],
                   'D': [0,0,1,1]
                   })


df = df.T.where(df.T != 1, df.columns).T.where(df != 0, np.nan)

Вывод:

     A    B    C    D
0  NaN  NaN    C  NaN
1    A  NaN  NaN  NaN
2  NaN    B    C    D
3    A  NaN  NaN    D
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...