У меня есть Pandas dataframe, который состоит из нескольких столбцов, ячейки которых могут содержать или не содержать строку. В качестве примера:

import numpy as np
import pandas as pd

df = pd.DataFrame({'A':['asfe','eseg','eesg','4dsf','','hdt','gase','gex','gsges','hhbr'],
                   'B':['','bdb','htsdg','','rdshg','th','tjf','','',''],
                   'C':['hrd','jyf','sef','hdsr','','','','','hdts','aseg'],
                   'D':['','','hdts','afse','nfd','','htf','','',''],
                   'E':['','','','','jftd','','','','jfdt','']})

... который выглядит так:

       A      B     C     D     E
0   asfe          hrd            
1   eseg    bdb   jyf            
2   eesg  htsdg   sef  hdts      
3   4dsf         hdsr  afse      
4         rdshg         nfd  jftd
5    hdt     th                  
6   gase    tjf         htf      
7    gex                         
8  gsges         hdts        jfdt
9   hhbr         aseg            

Я хочу создать столбец, который содержит двоичное представление того, содержит ли столбец строку или нет; Например, первая строка будет представлена как 10100.

Единственный способ сделать это -

  1. Создайте рабочий блок данных
  2. Пройдите по каждому столбцу, определяя, содержат ли ячейки какие-либо символы, и обозначая как 0/1.
  3. Объединить двоичные результаты в одну строку
  4. Скопируйте столбец с чистого кадра данных обратно в оригинал.

Это код, который я создал:

scratchdf = pd.DataFrame().reindex_like(df)

for col in df.columns.values:
    scratchdf[col] = df[col].str.contains(r'\w+',regex = True).astype(int)

scratchdf['bin'] =  scratchdf['A'].astype(str) + \
                    scratchdf['B'].astype(str) + \
                    scratchdf['C'].astype(str) + \
                    scratchdf['D'].astype(str) + \
                    scratchdf['E'].astype(str)

df = df.join(scratchdf['bin'])

... который создает окончательный фрейм данных:

       A      B     C     D     E    bin
0   asfe          hrd              10100
1   eseg    bdb   jyf              11100
2   eesg  htsdg   sef  hdts        11110
3   4dsf         hdsr  afse        10110
4         rdshg         nfd  jftd  01011
5    hdt     th                    11000
6   gase    tjf         htf        11010
7    gex                           10000
8  gsges         hdts        jfdt  10101
9   hhbr         aseg              10100

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

3
user1718097 27 Фев 2018 в 10:16

3 ответа

Лучший ответ

Проверьте пустую строку или сначала преобразуйте в bool, затем преобразуйте в int, str и, наконец, join или sum:

df['new'] = (df != '').astype(int).astype(str).apply(''.join, axis=1)

#faster alternative
df['new'] = (df != '').astype(int).astype(str).values.sum(axis=1)

print (df)

       A      B     C     D     E    new
0   asfe          hrd              10100
1   eseg    bdb   jyf              11100
2   eesg  htsdg   sef  hdts        11110
3   4dsf         hdsr  afse        10110
4         rdshg         nfd  jftd  01011
5    hdt     th                    11000
6   gase    tjf         htf        11010
7    gex                           10000
8  gsges         hdts        jfdt  10101
9   hhbr         aseg              10100

< Сильный > Задержки :

df = pd.concat([df] * 1000, ignore_index=True)

In [99]: %timeit df.astype(bool).astype(int).astype(str).values.sum(axis=1)
10 loops, best of 3: 155 ms per loop

In [100]: %timeit (df != '').astype(int).astype(str).values.sum(axis=1)
10 loops, best of 3: 158 ms per loop

In [101]: %timeit (df != '').astype(int).astype(str).apply(''.join, axis=1)
1 loop, best of 3: 330 ms per loop

In [102]: %timeit df.astype(bool).astype(int).astype(str).apply(''.join, axis=1)
1 loop, best of 3: 326 ms per loop

In [103]: %timeit df.astype(bool).astype(int).apply(lambda row: ''.join(str(i) for i in row), axis=1)
1 loop, best of 3: 210 ms per loop
2
jezrael 27 Фев 2018 в 07:38

Метод 1

a = np.where(df != "", "1", "0").astype("|S1")
df["bin"] = np.apply_along_axis(lambda x: x.tostring().decode("utf-8"), 1, a)

Метод 2

df["bin"] = np.append(
               np.where(df != "", "1", "0").astype("S1"), 
               np.array([["\n"]]).astype("S1").repeat(df.shape[0], axis=0), 
               axis=1
            ).tostring().decode("utf-8")[:-1].split("\n")

Метод 2 добавляет \n в конец массива numpy

array([[b'1', b'0', b'1', b'0', b'0', b'\n'],
       [b'1', b'1', b'1', b'0', b'0', b'\n'],
       [b'1', b'1', b'1', b'1', b'0', b'\n'],
       ...,
       [b'1', b'0', b'0', b'0', b'0', b'\n'],
       [b'1', b'0', b'1', b'0', b'1', b'\n'],
       [b'1', b'0', b'1', b'0', b'0', b'\n']], dtype='|S1')

Затем позвоните tostring и decode. Удалите последний "\ n", а затем разделите на "\ n".

Метод 3 (используется view ссылка: массив numpy символов в строку)

np.ascontiguousarray(
    np.where(df != "", "1", "0").astype("S1")
).view('|S5').astype(str)

Тайминги :

(Based on jezrael's setup df = pd.concat([df] * 1000, ignore_index=True))

# method 2
%timeit np.append(np.where(df != "", "1", "0").astype("S1"), np.array([["\n"]]).astype("S1").repeat(df.shape[0], axis=0), axis=1).tostring().decode("utf-8")[:-1].split("\n")
12.3 ms ± 175 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# method 3
%timeit np.ascontiguousarray(np.where(df != "", "1", "0").astype("S1")).view('|S5').astype(str)
12.8 ms ± 164 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# method 1 (slower)
%timeit np.apply_along_axis(lambda x: x.tostring().decode("utf-8"), 1, np.where(df != "", "1", "0").astype("S1"))
45 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Копии экспериментов Джезраэля.

In [99]: %timeit df.astype(bool).astype(int).astype(str).values.sum(axis=1)
28.9 ms ± 782 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [100]: %timeit (df != '').astype(int).astype(str).values.sum(axis=1)
29 ms ± 645 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [101]: %timeit (df != '').astype(int).astype(str).apply(''.join, axis=1)
168 ms ± 2.93 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [102]: %timeit df.astype(bool).astype(int).astype(str).apply(''.join, axis=1)
173 ms ± 7.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [103]: %timeit df.astype(bool).astype(int).apply(lambda row: ''.join(str(i) for i in row), axis=1)
159 ms ± 3.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1
Tai 27 Фев 2018 в 15:53

Вы можете использовать тот факт, что пустые строки соответствуют False, а непустые - True. Таким образом, приведение строкового фрейма данных к bool дает фрейм данных как true и false. Передача этого значения в int преобразует true в 1 и false в 0, а затем просто применяет операцию соединения к строкам:

df['binary'] = df.astype(bool).astype(int).apply(
    lambda row: ''.join(str(i) for i in row), axis=1)
print(df)

Результат:

       A      B     C     D     E  binary
0   asfe          hrd              10100
1   eseg    bdb   jyf              11100
2   eesg  htsdg   sef  hdts        11110
3   4dsf         hdsr  afse        10110
4         rdshg         nfd  jftd  01011
5    hdt     th                    11000
6   gase    tjf         htf        11010
7    gex                           10000
8  gsges         hdts        jfdt  10101
9   hhbr         aseg              10100

Редактировать: только что понял другой пользователь опубликовал в основном то же самое (также исправлена ошибка копирования)

Вот еще один способ использования генераторов:

def iterable_to_binary_mask(iterable):
    bools = (bool(i) for i in iterable)
    ints = (int(i) for i in bools)
    strs = (str(i) for i in ints)
    return ''.join(strs)

df['binary'] = df.apply(iterable_to_binary_mask, axis=1)

Это примерно в 3 раза медленнее, чем метод преобразования типов на моей машине, но должен использовать минимальное количество памяти.

2
evamicur 27 Фев 2018 в 07:47