我有一个熊猫数据框架,df_test。它包含一个列'size',以字节为单位表示大小。我已经计算了KB, MB和GB使用以下代码:

df_test = pd.DataFrame([
    {'dir': '/Users/uname1', 'size': 994933},
    {'dir': '/Users/uname2', 'size': 109338711},
])

df_test['size_kb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0, grouping=True) + ' KB')
df_test['size_mb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0 ** 2, grouping=True) + ' MB')
df_test['size_gb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0 ** 3, grouping=True) + ' GB')

df_test


             dir       size       size_kb   size_mb size_gb
0  /Users/uname1     994933      971.6 KB    0.9 MB  0.0 GB
1  /Users/uname2  109338711  106,776.1 KB  104.3 MB  0.1 GB

[2 rows x 5 columns]

我已经运行了超过120,000行,根据%timeit,每列大约需要2.97秒* 3 = ~9秒。

有什么办法能让它快点吗?例如,我可以从apply中一次返回一列并运行3次,我可以一次返回所有三列以插入到原始的数据框架中吗?

我发现的其他问题都希望接受多个值并返回一个值。我想取一个值并返回多个列。


当前回答

这是一种非常快速的方法,用apply和。只需将多个值作为列表返回,然后使用to_list()

import pandas as pd

dat = [ [i, 10*i] for i in range(100000)]

df = pd.DataFrame(dat, columns = ["a","b"])

def add_and_div(x):
    add = x + 3
    div = x / 3
    return [add, div]

start = time.time()
df[['c','d']] = df['a'].apply(lambda x: add_and_div(x)).to_list()
end = time.time()

print(end-start) # output: 0.27606

其他回答

简单明了:

def func(item_df):
  return [1,'Label 1'] if item_df['col_0'] > 0 else [0,'Label 0']
 
my_df[['col_1','col2']] = my_df.apply(func, axis=1,result_type='expand')

只是另一种可读的方式。这段代码将添加三个新列及其值,在apply函数中返回不带使用参数的序列。

def sizes(s):

    val_kb = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
    val_mb = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
    val_gb = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
    return pd.Series([val_kb,val_mb,val_gb],index=['size_kb','size_mb','size_gb'])

df[['size_kb','size_mb','size_gb']] = df.apply(lambda x: sizes(x) , axis=1)

一个来自https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.apply.html的通用示例

df.apply(lambda x: pd.Series([1, 2], index=['foo', 'bar']), axis=1)

#foo  bar
#0    1    2
#1    1    2
#2    1    2

这是一种非常快速的方法,用apply和。只需将多个值作为列表返回,然后使用to_list()

import pandas as pd

dat = [ [i, 10*i] for i in range(100000)]

df = pd.DataFrame(dat, columns = ["a","b"])

def add_and_div(x):
    add = x + 3
    div = x / 3
    return [add, div]

start = time.time()
df[['c','d']] = df['a'].apply(lambda x: add_and_div(x)).to_list()
end = time.time()

print(end-start) # output: 0.27606

通常,为了返回多个值,我就是这样做的

def gimmeMultiple(group):
    x1 = 1
    x2 = 2
    return array([[1, 2]])
def gimmeMultipleDf(group):
    x1 = 1
    x2 = 2
    return pd.DataFrame(array([[1,2]]), columns=['x1', 'x2'])
df['size'].astype(int).apply(gimmeMultiple)
df['size'].astype(int).apply(gimmeMultipleDf)

返回一个数据帧肯定有它的好处,但有时不是必需的。您可以查看apply()返回的内容,并对函数进行一些操作;)

您可以从包含新数据的应用函数返回一个Series,从而避免需要迭代三次。将axis=1传递给apply函数,将函数的大小应用到数据框架的每一行,返回一个要添加到新数据框架的序列。这个序列s包含新的值,以及原始数据。

def sizes(s):
    s['size_kb'] = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
    s['size_mb'] = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
    s['size_gb'] = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
    return s

df_test = df_test.append(rows_list)
df_test = df_test.apply(sizes, axis=1)