我试图基于分组数据框中的两列在Pandas数据框中创建一个新列。
具体来说,我正在尝试复制此R代码的输出:
library(data.table)
df = data.table(a = 1:6,
b = 7:12,
c = c('q', 'q', 'q', 'q', 'w', 'w')
)
df[, ab_weighted := sum(a)/sum(b), by = "c"]
df[, c('c', 'a', 'b', 'ab_weighted')]
输出:
到目前为止,我在Python中尝试了以下操作:
import pandas as pd
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
df.groupby(['c'])['a', 'b'].apply(lambda x: sum(x['a'])/sum(x['b']))
输出:
当我将上面代码中的apply
更改为transform
时,我得到一个错误:
TypeError:必须为整数
如果我只使用一列,则转换效果很好:
import pandas as pd
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
df.groupby(['c'])['a', 'b'].transform(lambda x: sum(x))
但是显然,这是不一样的答案:
是否有一种方法可以从Pandas中的data.table代码获取结果,而不必生成中间列(因为这样我可以在最后一列上使用transform
?
任何帮助,我们将不胜感激:)
答案 0 :(得分:3)
距您只有一步之遥。
data = [{"name": "IMG_1.jpg", "id": "53500"},{"name": "IMG_1.jpg", "id": "53501"}]
#=> ['53500', '53501']
答案 1 :(得分:3)
仅使用map
,R
和pandas
来修复代码仍具有不同的含义,这意味着并非每个R
函数都可以在pandas
中找到替代项
df.c.map(df.groupby(['c'])['a', 'b'].apply(lambda x: sum(x['a'])/sum(x['b'])))
Out[67]:
0 0.294118
1 0.294118
2 0.294118
3 0.294118
4 0.478261
5 0.478261
Name: c, dtype: float64
答案 2 :(得分:1)
这也可以。我不确定为什么,但是如果我让apply返回一个Series而不是Dataframe,我会得到一个错误。
df['ab_weighted'] = \
df.groupby('c', group_keys = False)['a', 'b'].apply(
lambda x: pd.Series(x.a.sum()/x.b.sum(),
index = x.index).to_frame()
).iloc[:,0]
print(df)
# output
# a b c ab_weighted
# 0 1 7 q 0.294118
# 1 2 8 q 0.294118
# 2 3 9 q 0.294118
# 3 4 10 q 0.294118
# 4 5 11 w 0.478261
# 5 6 12 w 0.478261
答案 3 :(得分:0)
2021-03-28 更新:我不推荐这个答案;我会推荐我的另一个,因为它更干净、更高效。
试试@BENY 的答案。如果不行,可能是索引不同造成的。
下面的解决方案很难看,而且更复杂,但它应该提供足够的线索来使任何数据框能够正常工作,而不仅仅是玩具数据框。这是 Pandas 的一个领域,其中 API 无疑是笨拙且容易出错的,有时根本没有一种干净的方法来获得任何有效的结果,而无需进行大量的跳槽。
诀窍是确保公共索引可用且名称相同。
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
df.reset_index(drop=True, inplace=True)
values = df.groupby(['c']).apply(lambda x: sum(x['a'])/sum(x['b']))
# Convert result to dataframe.
df_to_join = values.to_frame()
# Ensure indexes have common names.
df_to_join.index.set_names(["index"], inplace=True)
df.set_index("c", inplace=True)
df.index.set_names(["index"], inplace=True)
# Set column name of result we want.
df_to_join.rename(columns={0: "ab_weighted"}, inplace=True, errors='raise')
# Join result of groupby to original dataframe.
df_result = df.merge(df_to_join, on=["index"])
print(df_result)
# output
a b ab_weighted
index
q 1 7 0.294118
q 2 8 0.294118
q 3 9 0.294118
q 4 10 0.294118
w 5 11 0.478261
w 6 12 0.478261
并将索引转换回列 c
:
df_result.reset_index(inplace=True)
df_result.rename(columns={"index": "c"}, inplace=True)
答案 4 :(得分:0)
效果很好:
import numpy as np
import pandas as pd
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
def groupby_transform(df: pd.DataFrame, group_by_column: str, lambda_to_apply) -> np.array:
"""
Groupby and transform. Returns a column for the original dataframe.
:param df: Dataframe.
:param group_by_column: Column(s) to group by.
:param lambda_to_apply: Lambda.
:return: Column to append to original dataframe.
"""
df = df.reset_index(drop=True) # Dataframe index is now strictly in order of the rows in the original dataframe.
values = df.groupby(group_by_column).apply(lambda_to_apply)
values.sort_index(level=1, inplace=True) # Sorts result into order of original rows in dataframe (as groupby will undo that order when it groups).
result = np.array(values) # Sort rows into same order as original dataframe.
if result.shape[0] == 1: # e.g. if shape is (1,1003), make it (1003,).
result = result[0]
return result # Return column.
df["result"] = groupby_transform(df, "c", lambda x: x["a"].shift(1) + x["b"].shift(1))
输出:
a b c result
0 1 7 q NaN
1 2 8 q 8.0
2 3 9 q 10.0
3 4 10 q 12.0
4 5 11 w NaN
5 6 12 w 16.0
与上面的 Pandas extension 相同:
@pd.api.extensions.register_dataframe_accessor("ex")
class GroupbyTransform:
"""
Groupby and transform. Returns a column for the original dataframe.
"""
def __init__(self, pandas_obj):
self._validate(pandas_obj)
self._obj = pandas_obj
@staticmethod
def _validate(obj):
# TODO: Check that dataframe is sorted, throw if not.
pass
def groupby_transform(self, group_by_column: str, lambda_to_apply):
"""
Groupby and transform. Returns a column for the original dataframe.
:param df: Dataframe.
:param group_by_column: Column(s) to group by.
:param lambda_to_apply: Lambda.
:return: Column to append to original dataframe.
"""
df = self._obj.reset_index(drop=True) # Dataframe index is now strictly in order of the rows in the original dataframe.
values = df.groupby(group_by_column).apply(lambda_to_apply)
values.sort_index(level=1, inplace=True) # Sorts result into order of original rows in dataframe (as groupby will undo that order when it groups).
result = np.array(values)
if result.shape[0] == 1: # e.g. if shape is (1,1003), make it (1003,).
result = result[0]
return result
这给出了与之前相同的输出:
df["result"] = df.ex.groupby_transform("c", lambda x: x["a"].shift(1) + x["b"].shift(1))