Pandas:按行从 DataFrame 的特定列中选择值
Posted
技术标签:
【中文标题】Pandas:按行从 DataFrame 的特定列中选择值【英文标题】:Pandas: Select values from specific columns of a DataFrame by row 【发布时间】:2018-06-08 09:59:54 【问题描述】:给定一个具有多列的DataFrame,我们如何逐行从特定列中选择值来创建一个新的Series?
df = pd.DataFrame("A":[1,2,3,4],
"B":[10,20,30,40],
"C":[100,200,300,400])
columns_to_select = ["B", "A", "A", "C"]
目标:
[10, 2, 3, 400]
一种有效的方法是使用 apply 语句。
df["cols"] = columns_to_select
df.apply(lambda x: x[x.cols], axis=1)
不幸的是,这不是矢量化操作,并且在大型数据集上需要很长时间。任何想法将不胜感激。
【问题讨论】:
【参考方案1】:Pandas approach:
In [22]: df['new'] = df.lookup(df.index, columns_to_select)
In [23]: df
Out[23]:
A B C new
0 1 10 100 10
1 2 20 200 2
2 3 30 300 3
3 4 40 400 400
【讨论】:
落后一秒。 ;-) @Wen,是的,我知道这种感觉——对不起:) @MaxU 这正是我想要的。谢谢! @JasonSanchez,很高兴我能帮上忙 :)【参考方案2】:NumPy 方式
这是使用 advanced indexing
的矢量化 NumPy 方式 -
# Extract array data
In [10]: a = df.values
# Get integer based column IDs
In [11]: col_idx = np.searchsorted(df.columns, columns_to_select)
# Use NumPy's advanced indexing to extract relevant elem per row
In [12]: a[np.arange(len(col_idx)), col_idx]
Out[12]: array([ 10, 2, 3, 400])
如果df
的列名没有排序,我们需要使用sorter
参数和np.searchsorted
。为这样的通用 df
提取 col_idx
的代码将是:
# https://***.com/a/38489403/ @Divakar
def column_index(df, query_cols):
cols = df.columns.values
sidx = np.argsort(cols)
return sidx[np.searchsorted(cols,query_cols,sorter=sidx)]
所以,col_idx
会像这样获得 -
col_idx = column_index(df, columns_to_select)
进一步优化
分析它显示瓶颈是使用 np.searchsorted
处理字符串,这是 NumPy 通常的弱点,即对字符串不太好。因此,为了克服这个问题并使用列名是单个字母的特殊情况,我们可以快速将它们转换为数字,然后将它们提供给 searchsorted
以加快处理速度。
因此,对于列名是单个字母并已排序的情况,获取基于整数的列 ID 的优化版本将是 -
def column_index_singlechar_sorted(df, query_cols):
c0 = np.fromstring(''.join(df.columns), dtype=np.uint8)
c1 = np.fromstring(''.join(query_cols), dtype=np.uint8)
return np.searchsorted(c0, c1)
这给了我们解决方案的修改版本,就像这样 -
a = df.values
col_idx = column_index_singlechar_sorted(df, columns_to_select)
out = pd.Series(a[np.arange(len(col_idx)), col_idx])
时间安排 -
In [149]: # Setup df with 26 uppercase column letters and many rows
...: import string
...: df = pd.DataFrame(np.random.randint(0,9,(1000000,26)))
...: s = list(string.uppercase[:df.shape[1]])
...: df.columns = s
...: idx = np.random.randint(0,df.shape[1],len(df))
...: columns_to_select = np.take(s, idx).tolist()
# With df.lookup from @MaxU's soln
In [150]: %timeit pd.Series(df.lookup(df.index, columns_to_select))
10 loops, best of 3: 76.7 ms per loop
# With proposed one from this soln
In [151]: %%timeit
...: a = df.values
...: col_idx = column_index_singlechar_sorted(df, columns_to_select)
...: out = pd.Series(a[np.arange(len(col_idx)), col_idx])
10 loops, best of 3: 59 ms per loop
鉴于 df.lookup
解决了一般情况,这可能是一个更好的选择,但本文中显示的其他可能的优化也很方便!
【讨论】:
以上是关于Pandas:按行从 DataFrame 的特定列中选择值的主要内容,如果未能解决你的问题,请参考以下文章