pandas 在列上使用 cumsum 但重置计数
pandas use cumsum over columns but reset count
This post and this post 让我接近了,但我没能解决我的问题。
我有一个 df 看起来像:
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 0.0 active 0.0 0.0
1 0.0 active 0.0 active
2 0.0 0.0 0.0 0.0
我想计算每行中的零并将它们放入字符串中以对数据进行编码,但只要没有连续的零,就需要重置计数。
对于上面的 df,输出 df 看起来像:
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 inactive_1 active inactive_1 inactive_2
1 inactive_1 active inactive_1 active
2 inactive_1 inactive_2 inactive_3 inactive_4
这个函数让我非常接近,但不考虑重置 cumsum,它只是对行中所有零实例的总和。
def inactive(s):
np.where(s == 0, 'inactive_' + (s.eq(0).cumsum()).astype(str), s)
df.apply(inactive, 1)
有点迂回,但这可以通过对每一行应用 groupby
操作,然后使用 np.where
有选择地将您的值应用到原始值来完成。
def f(x):
return x.groupby(x.ne(x.shift()).cumsum()).cumcount() + 1
i = df.apply(pd.to_numeric, errors='coerce')
j = 'inactive_' + i.apply(f, axis=1).astype(str)
df[:] = np.where(i.ne(0), df.values, j)
df
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 inactive_1 active inactive_1 inactive_2
1 inactive_1 active inactive_1 active
2 inactive_1 inactive_2 inactive_3 inactive_4
您可以使用:
#convert to numeric, NaNs for non numeric
df1 = df.apply(pd.to_numeric, errors='coerce')
#count consecutive values with reset
a = df1 == 0
b = a.cumsum(axis=1)
c = b-b.where(~a, axis=1).ffill(axis=1).fillna(0).astype(int)
print (c)
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 1 0 1 2
1 1 0 1 0
2 1 2 3 4
#replace by mask
df = df.mask(c != 0, 'inactive_' + c.astype(str))
print (df)
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 inactive_1 active inactive_1 inactive_2
1 inactive_1 active inactive_1 active
2 inactive_1 inactive_2 inactive_3 inactive_4
时间:
np.random.seed(425)
df = pd.DataFrame(np.random.choice([0, 'active'], size=(100000, 300)))
In [4]: %timeit (jez(df))
1 loop, best of 3: 1min 40s per loop
In [5]: %timeit col(df)
1 loop, best of 3: 5min 54s per loop
def jez(df):
df1 = df.apply(pd.to_numeric, errors='coerce')
#count consecutive values
a = df1 == 0
b = a.cumsum(axis=1)
c = b-b.where(~a, axis=1).ffill(axis=1).fillna(0).astype(int)
#replace by mask
return df.mask(c != 0, 'inactive_' + c.astype(str))
def f(x):
return x.groupby(x.ne(x.shift()).cumsum()).cumcount() + 1
def col(df):
i = df.apply(pd.to_numeric, errors='coerce')
j = 'inactive_' + i.apply(f, axis=1).astype(str)
df[:] = np.where(i.ne(0), df.values, j)
return(df)
警告:
性能真的取决于数据。
This post and this post 让我接近了,但我没能解决我的问题。
我有一个 df 看起来像:
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 0.0 active 0.0 0.0
1 0.0 active 0.0 active
2 0.0 0.0 0.0 0.0
我想计算每行中的零并将它们放入字符串中以对数据进行编码,但只要没有连续的零,就需要重置计数。
对于上面的 df,输出 df 看起来像:
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 inactive_1 active inactive_1 inactive_2
1 inactive_1 active inactive_1 active
2 inactive_1 inactive_2 inactive_3 inactive_4
这个函数让我非常接近,但不考虑重置 cumsum,它只是对行中所有零实例的总和。
def inactive(s):
np.where(s == 0, 'inactive_' + (s.eq(0).cumsum()).astype(str), s)
df.apply(inactive, 1)
有点迂回,但这可以通过对每一行应用 groupby
操作,然后使用 np.where
有选择地将您的值应用到原始值来完成。
def f(x):
return x.groupby(x.ne(x.shift()).cumsum()).cumcount() + 1
i = df.apply(pd.to_numeric, errors='coerce')
j = 'inactive_' + i.apply(f, axis=1).astype(str)
df[:] = np.where(i.ne(0), df.values, j)
df
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 inactive_1 active inactive_1 inactive_2
1 inactive_1 active inactive_1 active
2 inactive_1 inactive_2 inactive_3 inactive_4
您可以使用:
#convert to numeric, NaNs for non numeric
df1 = df.apply(pd.to_numeric, errors='coerce')
#count consecutive values with reset
a = df1 == 0
b = a.cumsum(axis=1)
c = b-b.where(~a, axis=1).ffill(axis=1).fillna(0).astype(int)
print (c)
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 1 0 1 2
1 1 0 1 0
2 1 2 3 4
#replace by mask
df = df.mask(c != 0, 'inactive_' + c.astype(str))
print (df)
2017-04-03 2017-04-04 2017-04-05 2017-04-06
id
0 inactive_1 active inactive_1 inactive_2
1 inactive_1 active inactive_1 active
2 inactive_1 inactive_2 inactive_3 inactive_4
时间:
np.random.seed(425)
df = pd.DataFrame(np.random.choice([0, 'active'], size=(100000, 300)))
In [4]: %timeit (jez(df))
1 loop, best of 3: 1min 40s per loop
In [5]: %timeit col(df)
1 loop, best of 3: 5min 54s per loop
def jez(df):
df1 = df.apply(pd.to_numeric, errors='coerce')
#count consecutive values
a = df1 == 0
b = a.cumsum(axis=1)
c = b-b.where(~a, axis=1).ffill(axis=1).fillna(0).astype(int)
#replace by mask
return df.mask(c != 0, 'inactive_' + c.astype(str))
def f(x):
return x.groupby(x.ne(x.shift()).cumsum()).cumcount() + 1
def col(df):
i = df.apply(pd.to_numeric, errors='coerce')
j = 'inactive_' + i.apply(f, axis=1).astype(str)
df[:] = np.where(i.ne(0), df.values, j)
return(df)
警告:
性能真的取决于数据。