非数字数据的滚动多数

rolling majority on non-numeric data

给定一个数据框:

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

我想用 'a' 周围的大多数值替换列 'a' 中的每个值。对于数值数据,我可以这样做:

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

我将不得不处理 NaNs,但除此之外,这或多或少是我想要的...除了,我想对非 -数值列,但是 Pandas 似乎不支持这个:

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 上工作,最后从代码转换回标签,但这看起来真的很复杂。

是否有easier/more自然的解决方案?

(顺便说一句:我仅限于 NumPy 1.8.2,所以我必须使用 itemfreq 而不是 unique,请参阅 here。)

这是一种通过定义您自己的滚动应用函数来实现的方法。

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

这里有一个方法,使用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