2016-05-30 2 views
1

EDIT (благодаря Jezrael вести меня на пути, чтобы сделать это):Сделать dataframe с str.count() по индексу

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

Для этого я создать dataframe (потому что я чувствую, что это может быть эффективным способом сделать это), который содержит в каждой строке раскол номер телефона:

numbers = ['0615260518','0815465948','0215616235','0415291826'] 
df = pd.DataFrame([list(number) for number in numbers]) 


Out[1]: 
    0 1 2 3 4 5 6 7 8 9 
0 0 6 1 5 2 6 0 5 1 8 
1 0 8 1 5 4 6 5 9 4 8 
2 0 2 1 5 6 1 6 2 3 5 
3 0 4 1 5 2 9 1 8 2 6 

Тогда мне нужно заполнить его нужный показатель. Желаемый результат:

Out[2]: 
      0 1 2 3 4 5 6 7 8 9 
0615260518 2 2 1 0 0 2 2 0 1 0 
0815465948 1 1 0 0 2 2 1 0 2 1 
0215616235 1 2 2 1 0 2 2 0 0 0 
0415291826 1 2 2 0 1 1 1 0 1 1 

Вот чертов не вещий способ ее достижения:

for num in df.index: 
    for col in df.columns: 
     df.ix[num,col] = num.count(str(col)) 

Слишком долго, чтобы вычислить, то Jezrael предложил мне это решение:

df.apply(lambda x: x.value_counts(), axis=1).fillna(0).astype(int) 

Который является лучше, но все еще слишком долго. Так что я пытался заменить value_counts, которые не предназначены для маленьких dataframes:

df.apply(lambda x: digit_count(''.join(x)), axis=1) 

Где:

def digit_count(number): 
    my_string = list(number.lower()) 
    my_dict = [] 
    for i in np.arange(10): 
     my_dict.append(my_string.count(str(i))) 
    return my_dict 

Что примерно в 3 раза быстрее. Но есть ли способ сделать это еще быстрее (я считаю, что эта итерация не является оптимальной).

ответ

2

Вы можете сначала преобразовать indexto_series, так как apply не работает с index. Последний applyvalue_counts, fillna и приведение к int по astype:

a = (df.index.to_series().apply(lambda x: pd.Series(list(x)))) 
print (a) 
      0 1 2 3 4 5 6 7 8 9 
0615260518 0 6 1 5 2 6 0 5 1 8 
0815465948 0 8 1 5 4 6 5 9 4 8 
0215616235 0 2 1 5 6 1 6 2 3 5 
0415291826 0 4 1 5 2 9 1 8 2 6 

print (a.apply(lambda x: x.value_counts(), axis=1).fillna(0).astype(int)) 

      0 1 2 3 4 5 6 8 9 
0615260518 2 2 1 0 0 2 2 1 0 
0815465948 1 1 0 0 2 2 1 2 1 
0215616235 1 2 2 1 0 2 2 0 0 
0415291826 1 2 2 0 1 1 1 1 1 

EDIT:

from collections import Counter 
print (pd.DataFrame([x for x in a.apply(Counter, axis=1)])) 
    0 1 2 3 4 5 6 8 9 
0 2 2 1.0 NaN NaN 2 2 1.0 NaN 
1 1 1 NaN NaN 2.0 2 1 2.0 1.0 
2 1 2 2.0 1.0 NaN 2 2 NaN NaN 
3 1 2 2.0 NaN 1.0 1 1 1.0 1.0 

Timings (len(df)=4):

In [288]: %timeit (a.apply(lambda x: x.value_counts(), axis=1)) 
100 loops, best of 3: 3.74 ms per loop 

In [289]: %timeit (pd.DataFrame([x for x in a.apply(Counter, axis=1)])) 
1000 loops, best of 3: 1.27 ms per loop 

(len(df)=4k):

In [296]: %timeit (pd.DataFrame([x for x in a.apply(Counter, axis=1)])) 
10 loops, best of 3: 87 ms per loop 

In [297]: %timeit (a.apply(lambda x: x.value_counts(), axis=1)) 
1 loop, best of 3: 2.45 s per loop 
+0

Благодарим за ответ. Мне действительно не нужно поддерживать индекс, поэтому для первого шага следующее выглядит более эффективным: a = pd.DataFrame ([список (число) для числа в числах]). Что касается второй части, для вычисления все равно требуется около 3 минут, не так ли? – ysearka

+0

Отлично, встречная дорога отлично подходит для этой проблемы, спасибо большое! – ysearka

+0

Благодарим вас за выживание. Приятный день. – jezrael

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