如果我在pandas中有一个DataFrame,它看起来像:
A B C
0 1 NaN 2
1 NaN 3 NaN
2 NaN 4 5
3 NaN NaN NaN
Run Code Online (Sandbox Code Playgroud)
如何从每一行获取第一个非空值?例如,对于上述情况,我想得到:( [1, 3, 4, None]或等效系列).
And*_*nes 35
你不需要乱用first_valid_index:
df.bfill(axis=1).iloc[:, 0]
Run Code Online (Sandbox Code Playgroud)
Joe*_*ron 11
我将在这里权衡,因为我认为这比任何提议的方法都要快得多.以矢量化的方式给出结果每行中argmin第一个False值的索引np.isnan,这是困难的部分.它仍然依赖于Python循环来提取值,但查找非常快:
def get_first_non_null(df):
a = df.values
col_index = np.isnan(a).argmin(axis=1)
return [a[row, col] for row, col in enumerate(col_index)]
Run Code Online (Sandbox Code Playgroud)
编辑:这是一个完全矢量化的解决方案,根据输入的形状,可以再次更快.更新了下面的基准测试
def get_first_non_null_vec(df):
a = df.values
n_rows, n_cols = a.shape
col_index = np.isnan(a).argmin(axis=1)
flat_index = n_cols * np.arange(n_rows) + col_index
return a.ravel()[flat_index]
Run Code Online (Sandbox Code Playgroud)
如果一行完全为null,则相应的值也将为null.这是针对unutbu解决方案的一些基准测试:
df = pd.DataFrame(np.random.choice([1, np.nan], (10000, 1500), p=(0.01, 0.99)))
#%timeit df.stack().groupby(level=0).first().reindex(df.index)
%timeit get_first_non_null(df)
%timeit get_first_non_null_vec(df)
1 loops, best of 3: 220 ms per loop
100 loops, best of 3: 16.2 ms per loop
100 loops, best of 3: 12.6 ms per loop
In [109]:
df = pd.DataFrame(np.random.choice([1, np.nan], (100000, 150), p=(0.01, 0.99)))
#%timeit df.stack().groupby(level=0).first().reindex(df.index)
%timeit get_first_non_null(df)
%timeit get_first_non_null_vec(df)
1 loops, best of 3: 246 ms per loop
10 loops, best of 3: 48.2 ms per loop
100 loops, best of 3: 15.7 ms per loop
df = pd.DataFrame(np.random.choice([1, np.nan], (1000000, 15), p=(0.01, 0.99)))
%timeit df.stack().groupby(level=0).first().reindex(df.index)
%timeit get_first_non_null(df)
%timeit get_first_non_null_vec(df)
1 loops, best of 3: 326 ms per loop
1 loops, best of 3: 326 ms per loop
10 loops, best of 3: 35.7 ms per loop
Run Code Online (Sandbox Code Playgroud)
EdC*_*ica 10
这是一个非常混乱的方法,首先用于first_valid_index获取有效列,将返回的系列转换为数据帧,以便我们可以按apply行调用并使用它来索引回原始df:
In [160]:
def func(x):
if x.values[0] is None:
return None
else:
return df.loc[x.name, x.values[0]]
pd.DataFrame(df.apply(lambda x: x.first_valid_index(), axis=1)).apply(func,axis=1)
?
Out[160]:
0 1
1 3
2 4
3 NaN
dtype: float64
Run Code Online (Sandbox Code Playgroud)
编辑
一种稍微清洁的方式:
In [12]:
def func(x):
if x.first_valid_index() is None:
return None
else:
return x[x.first_valid_index()]
df.apply(func, axis=1)
Out[12]:
0 1
1 3
2 4
3 NaN
dtype: float64
Run Code Online (Sandbox Code Playgroud)
这是另一种方法:
In [183]: df.stack().groupby(level=0).first().reindex(df.index)
Out[183]:
0 1
1 3
2 4
3 NaN
dtype: float64
Run Code Online (Sandbox Code Playgroud)
这里的想法是用于stack将列移动到行索引级别:
In [184]: df.stack()
Out[184]:
0 A 1
C 2
1 B 3
2 B 4
C 5
dtype: float64
Run Code Online (Sandbox Code Playgroud)
现在,如果按第一行级别(即原始索引)进行分组,并从每个组中获取第一个值,则基本上可以得到所需的结果:
In [185]: df.stack().groupby(level=0).first()
Out[185]:
0 1
1 3
2 4
dtype: float64
Run Code Online (Sandbox Code Playgroud)
我们需要做的就是重新索引结果(使用原始索引),以便包含完全NaN的行:
df.stack().groupby(level=0).first().reindex(df.index)
Run Code Online (Sandbox Code Playgroud)
这并不是什么新鲜事,但它结合了@yangie 方法的最佳部分与列表理解,以及我认为最容易理解的@EdChumdf.apply方法。
首先,我们想从哪些列中选择我们的值?
In [95]: pick_cols = df.apply(pd.Series.first_valid_index, axis=1)
In [96]: pick_cols
Out[96]:
0 A
1 B
2 B
3 None
dtype: object
Run Code Online (Sandbox Code Playgroud)
现在我们如何选择值?
In [100]: [df.loc[k, v] if v is not None else None
....: for k, v in pick_cols.iteritems()]
Out[100]: [1.0, 3.0, 4.0, None]
Run Code Online (Sandbox Code Playgroud)
这没关系,但我们真的希望索引与原始索引匹配DataFrame:
In [98]: pd.Series({k:df.loc[k, v] if v is not None else None
....: for k, v in pick_cols.iteritems()})
Out[98]:
0 1
1 3
2 4
3 NaN
dtype: float64
Run Code Online (Sandbox Code Playgroud)
groupby在axis=1如果我们传递一个返回相同值的可调用函数,我们会将所有列分组在一起。这允许我们使用groupby.agg它为我们提供了first使这变得容易的方法
df.groupby(lambda x: 'Z', 1).first()
Z
0 1.0
1 3.0
2 4.0
3 NaN
Run Code Online (Sandbox Code Playgroud)
这将返回一个数据框,其中包含我在可调用对象中返回的内容的列名称
lookup,notna, 和idxmaxdf.lookup(df.index, df.notna().idxmax(1))
array([ 1., 3., 4., nan])
Run Code Online (Sandbox Code Playgroud)
argmin和切片v = df.values
v[np.arange(len(df)), np.isnan(v).argmin(1)]
array([ 1., 3., 4., nan])
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12456 次 |
| 最近记录: |