2016-09-09 4 views
0

У меня есть большой CSV с тремя строками в строке в этой форме:Как перекодировать и считать эффективно

a,c,d 
c,a,e 
f,g,f 
a,c,b 
c,a,d 
b,f,s 
c,a,c 

я прочитал в первых двух столбцах перекодировать строки в целые числа, а затем удалить дубликаты подсчета, сколько копий каждой строки были следующими:

import pandas as pd 
df = pd.read_csv("test.csv", usecols=[0,1], prefix="ID_", header=None) 
letters = set(df.values.flat) 
df.replace(to_replace=letters, value=range(len(letters)), inplace=True) 
df1 = df.groupby(['ID_0', 'ID_1']).size().rename('count').reset_index() 
print df1 

Это дает:

ID_0 ID_1 count 
0  0  1  2 
1  1  0  3 
2  2  4  1 
3  4  3  1 

, который я именно то, что мне нужно.

Однако, поскольку мои данные большие, я хотел бы сделать два улучшения.

  • Как я могу выполнить группу, а затем перекодировать, а не наоборот? Проблема в том, что я не могу выполнить df1 [['ID_0', 'ID_0']]. Replace (to_replace = буквы, значение = диапазон (len (буквы)), inplace = True). Это дает ошибку «Значение пытается установить на копии среза из DataFrame»
  • Как избежать создания df1? То есть все это на месте.

ответ

3

Мне нравится использовать sklearn.preprocessing.LabelEncoder сделать букву для преобразования цифр:

from sklearn.preprocessing import LabelEncoder 

# Perform the groupby (before converting letters to digits). 
df = df.groupby(['ID_0', 'ID_1']).size().rename('count').reset_index() 

# Initialize the LabelEncoder. 
le = LabelEncoder() 
le.fit(df[['ID_0', 'ID_1']].values.flat) 

# Convert to digits. 
df[['ID_0', 'ID_1']] = df[['ID_0', 'ID_1']].apply(le.transform) 

Полученный выход:

ID_0 ID_1 count 
0  0  2  2 
1  1  3  1 
2  2  0  3 
3  3  4  1 

Если вы хотите вернуться к письмам на более позднем этапе время, вы можете использовать le.inverse_transform:

df[['ID_0', 'ID_1']] = df[['ID_0', 'ID_1']].apply(le.inverse_transform) 

Какие карты обратно, как и ожидалось:

ID_0 ID_1 count 
0 a c  2 
1 b f  1 
2 c a  3 
3 f g  1 

Если вы просто хотите знать, какая цифра соответствует какая буква, вы можете посмотреть на атрибут le.classes_.Это даст вам множество писем, которые индексируются цифрой она кодирует для:

le.classes_ 

['a' 'b' 'c' 'f' 'g'] 

Для более наглядного представления, вы можете бросить в серии:

pd.Series(le.classes_) 

0 a 
1 b 
2 c 
3 f 
4 g 

Настройку время

Используя большую версию выборки данных и следующие настройки:

df2 = pd.concat([df]*10**5, ignore_index=True) 

def root(df): 
    df = df.groupby(['ID_0', 'ID_1']).size().rename('count').reset_index() 
    le = LabelEncoder() 
    le.fit(df[['ID_0', 'ID_1']].values.flat) 
    df[['ID_0', 'ID_1']] = df[['ID_0', 'ID_1']].apply(le.transform) 
    return df 

def pir2(df): 
    unq = np.unique(df) 
    mapping = pd.Series(np.arange(unq.size), unq) 
    return df.stack().map(mapping).unstack() \ 
     .groupby(df.columns.tolist()).size().reset_index(name='count') 

я получаю следующие тайминги:

%timeit root(df2) 
10 loops, best of 3: 101 ms per loop 

%timeit pir2(df2) 
1 loops, best of 3: 1.69 s per loop 
+0

Это очень приятно! Думаю, я скопирую твою идею. – eleanora

2

Новый ответ

unq = np.unique(df) 
mapping = pd.Series(np.arange(unq.size), unq) 

df.stack().map(mapping).unstack() \ 
    .groupby(df.columns.tolist()).size().reset_index(name='count') 

enter image description here

Старый Ответ

df.stack().rank(method='dense').astype(int).unstack() \ 
    .groupby(df.columns.tolist()).size().reset_index(name='count') 

enter image description here

+0

Мне нужно хранить отображение от исходных строк до целых чисел. Это дается явно в методе в моем вопросе. Можете ли вы получить его и от своего? – eleanora

+0

Маленькая вещь: мне нужно, чтобы идентификаторы начинались с 0, а не 1. – eleanora

Смежные вопросы