为了账号安全,请及时绑定邮箱和手机立即绑定

有效地从 Pandas DataFrame 创建一系列元组

有效地从 Pandas DataFrame 创建一系列元组

30秒到达战场 2021-07-15 16:10:40
我正在使用apply()现有 DataFrame 的值构造一系列元组。我需要构造元组中值的特定顺序,并将NaN除一列之外的所有列替换为'{}'.以下函数可以产生所需的结果,但执行速度相当慢:def build_insert_tuples_series(row):    # Here I attempt to handle ordering the final tuple    # I must also replace NaN with "{}" for all but v2 column.    vals = [row['v2']]    row_sans_v2 = row.drop(labels=['v2'])    row_sans_v2.fillna("{}", inplace=True)    res = [val for val in row_sans_token]    vals += res    return tuple(vals)def generate_insert_values_series(df):    df['insert_vals'] = df.apply(lambda x: build_insert_tuples_series(x), axis=1)    return df['insert_vals']原始数据帧:    id   v1    v20  1.0  foo  quux1  2.0  bar   foo2  NaN  NaN   baz调用时产生的数据帧generate_insert_values_series(df):最终元组的顺序逻辑是 (v2, ..all_other_columns..)    id   v1    v2       insert_vals0  1.0  foo  quux  (quux, 1.0, foo)1  2.0  bar   foo   (foo, 2.0, bar)2  NaN  NaN   baz     (baz, {}, {})计时函数以生成结果数据帧:%%timeitgenerate_insert_values_series(df)100 loops, best of 3: 2.69 ms per loop我觉得可能有一种方法可以更有效地构建系列,但不确定如何使用矢量化或其他方法来优化操作。
查看完整描述

3 回答

?
温温酱

TA贡献1752条经验 获得超4个赞

zip, get, mask, fillna, 和sorted

一件物有所值


df.assign(

    insert_vals=

    [*zip(*map(df.mask(df.isna(), {}).get, sorted(df, key=lambda x: x != 'v2')))])


    id   v1    v2       insert_vals

0  1.0  foo  quux  (quux, 1.0, foo)

1  2.0  bar   foo   (foo, 2.0, bar)

2  NaN  NaN   baz     (baz, {}, {})

少一行字


get = df.mask(df.isna(), {}).get

key = lambda x: x != 'v2'

cols = sorted(df, key=key)


df.assign(insert_vals=[*zip(*map(get, cols))])


    id   v1    v2       insert_vals

0  1.0  foo  quux  (quux, 1.0, foo)

1  2.0  bar   foo   (foo, 2.0, bar)

2  NaN  NaN   baz     (baz, {}, {})

这应该适用于旧版 python


get = df.mask(df.isna(), {}).get

key = lambda x: x != 'v2'

cols = sorted(df, key=key)


df.assign(insert_vals=zip(*map(get, cols)))


查看完整回答
反对 回复 2021-07-21
?
莫回无

TA贡献1865条经验 获得超7个赞

你不应该要做到这一点,因为你的新系列将失去所有的矢量化功能。


但是,如果必须,您可以apply通过使用pd.DataFrame.itertuples列表推导式或map. 唯一的复杂之处是重新排序列,您可以通过转换为list:


df = pd.concat([df]*10000, ignore_index=True)


col_lst = df.columns.tolist()

cols = [col_lst.pop(col_lst.index('v2'))] + col_lst


%timeit list(df[cols].itertuples(index=False))  # 31.3 ms per loop

%timeit [tuple(x) for x in df[cols].values]     # 74 ms per loop

%timeit list(map(tuple, df[cols].values))       # 73 ms per loop

上面的基准测试是在 Python 3.6.0 上进行的,但您可能会发现这些比apply在 2.7 上更有效。请注意,list最终版本不需要转换,因为在 v2.7 中map返回 a list。


如果绝对必要,您可以fillna通过一系列:


s = pd.Series([{} for _ in range(len(df.index))], index=df.index)


for col in df[cols]:

    df[cols].fillna(s)


查看完整回答
反对 回复 2021-07-21
?
MYYA

TA贡献1868条经验 获得超4个赞

首先,您可以使用numpy替换null值dicts


import pandas as pd

import numpy as np


temp = pd.DataFrame({'id':[1,2, None], 'v1':['foo', 'bar', None], 'v2':['quux', 'foo', 'bar']})


def replace_na(col): 

    return np.where(temp[col].isnull(), '{}', temp[col])


def generate_tuple(df):

    df['id'], df['v1'] = replace_na('id'), replace_na('v1')

    return df.apply(lambda x: tuple([x['v2'], x['id'], x['v1']]), axis=1)

你的收获是


%%timeit

temp['insert_tuple'] = generate_tuple(temp)

>>>> 1000 loops, best of 3 : 1ms per loop

如果您将其更改generate_tuple return为类似


def generate_tuple(df):

    df['id'], df['v1'] = replace_na('id'), replace_na('v1')

    return list(zip(df['v2'], df['id'], df['v1']))

你的收益变成:


%%timeit

temp['insert_tuple'] = generate_tuple(temp)

1000 loops, best of 3 : 674 µs per loop


查看完整回答
反对 回复 2021-07-21
  • 3 回答
  • 0 关注
  • 272 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信