2015-07-03 5 views
1

дана dataframe:качению большинство на нечисловых данных

df = pd.DataFrame({'a' : [1,1,1,1,1,2,1,2,2,2,2]}) 

Я хотел бы заменить все значения в столбце «а» большинство значений вокруг «а». Для числовых данных, я могу это сделать:

def majority(window): 
    freqs = scipy.stats.itemfreq(window) 
    max_votes = freqs[:,1].argmax() 
    return freqs[max_votes,0] 

df['a'] = pd.rolling_apply(df['a'], 3, majority) 

И я получаю:

In [43]: df 
Out[43]: 
    a 
0 NaN 
1 NaN 
2 1 
3 1 
4 1 
5 1 
6 1 
7 2 
8 2 
9 2 
10 2 

мне придется иметь дело с NaN с, но, кроме того, это более или менее то, что я хочу ... кроме этого, я хотел бы сделать то же самое с нечисловыми колонками, но панды, кажется, не поддерживают это:

In [47]: df['b'] = list('aaaababbbba') 
In [49]: df['b'] = pd.rolling_apply(df['b'], 3, majority) 
--------------------------------------------------------------------------- 
ValueError        Traceback (most recent call last) 
<ipython-input-49-507f45aab92c> in <module>() 
----> 1 df['b'] = pd.rolling_apply(df['b'], 3, majority) 

/usr/local/lib/python2.7/dist-packages/pandas/stats/moments.pyc in rolling_apply(arg, window, func, min_periods, freq, center, args, kwargs) 
    751   return algos.roll_generic(arg, window, minp, offset, func, args, kwargs) 
    752  return _rolling_moment(arg, window, call_cython, min_periods, freq=freq, 
--> 753       center=False, args=args, kwargs=kwargs) 
    754 
    755 

/usr/local/lib/python2.7/dist-packages/pandas/stats/moments.pyc in _rolling_moment(arg, window, func, minp, axis, freq, center, how, args, kwargs, **kwds) 
    382  arg = _conv_timerule(arg, freq, how) 
    383 
--> 384  return_hook, values = _process_data_structure(arg) 
    385 
    386  if values.size == 0: 

/usr/local/lib/python2.7/dist-packages/pandas/stats/moments.pyc in _process_data_structure(arg, kill_inf) 
    433 
    434  if not issubclass(values.dtype.type, float): 
--> 435   values = values.astype(float) 
    436 
    437  if kill_inf: 

ValueError: could not convert string to float: a 

Я попытался преобразование a в Categorical, но даже тогда я получаю ту же ошибку. Я могу сначала преобразовать в Categorical, работать с codes и, наконец, конвертировать обратно из кодов в метки, но это кажется действительно запутанным.

Есть ли более простое или более естественное решение?

(BTW: Я ограничен NumPy 1.8.2, так что я должен использовать itemfreq вместо unique см here.)

ответ

1

Вот один из способов сделать это, определив свою собственную функцию качения.

import pandas as pd 

df = pd.DataFrame({'a' : [1,1,1,1,1,2,1,2,2,2,2]}) 
df['b'] = np.where(df.a == 1, 'A', 'B') 

print(df) 

Out[60]: 
    a b 
0 1 A 
1 1 A 
2 1 A 
3 1 A 
4 1 A 
5 2 B 
6 1 A 
7 2 B 
8 2 B 
9 2 B 
10 2 B 

def get_mode_from_Series(series): 
    return series.value_counts().index[0] 

def my_rolling_apply_char(frame, window, func): 
    index = frame.index[window-1:] 
    values = [func(frame.iloc[i:i+window]) for i in range(len(frame)-window+1)] 
    return pd.Series(data=values, index=index).reindex(frame.index) 

my_rolling_apply_char(df.b, 3, get_mode_from_Series) 

Out[61]: 
0  NaN 
1  NaN 
2  A 
3  A 
4  A 
5  A 
6  A 
7  B 
8  B 
9  B 
10  B 
dtype: object 
+0

небось, проходящему через категоричен является наименее громоздким способом сделать это, Afterall, но я Принимаю это, потому что я прямо просил что-то другое. BTW: Я неясен во всем индексировании бизнеса в Pandas: не могли бы вы объяснить, что делают бит 'index = index' и' reindex()? –

+1

@JohannesBauer 'index = index' заставляет возвращенный' pd.Series' иметь индекс '2,3, ..., 10', а не целочисленный индекс по умолчанию' 0,1, ..., 8'. Последняя часть 'reindex' пытается выровнять индекс с исходным' df' и заполняет невидимый индекс 'NaN'. –

+0

Спасибо большое, @Jianxun. –

0

Это не выглядит, как она поддерживается напрямую, но вы могли бы сделать что-то вроде это:

import collections 

pd.concat([df.shift(i) for i in range(3)], axis=1).apply(lambda g: collections.Counter(g).most_common(1)[0][0], axis=1) 

Вот что происходит:

[df.shift(i) for i in range(3)] 

- это список вашего DataFrame, сдвинутого по-другому (0, 1 и 2, в данном случае).

pd.concat([df.shift(i) for i in range(3)], axis=1) 

объединяет их по горизонтали.

pd.concat(...).apply(..., axis=1) 

применяет функцию к строкам конкатенации.

6

Вот способ, с помощью pd.Categorical:

import scipy.stats as stats 
import pandas as pd 

def majority(window): 
    freqs = stats.itemfreq(window) 
    max_votes = freqs[:,1].argmax() 
    return freqs[max_votes,0] 

df = pd.DataFrame({'a' : [1,1,1,1,1,2,1,2,2,2,2]}) 
df['a'] = pd.rolling_apply(df['a'], 3, majority) 
df['b'] = list('aaaababbbba') 

cat = pd.Categorical(df['b']) 
df['b'] = pd.rolling_apply(cat.codes, 3, majority) 
df['b'] = df['b'].map(pd.Series(cat.categories)) 
print(df) 

дает

 a b 
0 NaN NaN 
1 NaN NaN 
2 1 a 
3 1 a 
4 1 a 
5 1 a 
6 1 b 
7 2 b 
8 2 b 
9 2 b 
10 2 b 
+0

Это хорошее обходное решение, использующее категории «коды» и «категории» для преобразования в числовые и обратные. +1 – pansen