【问题标题】:How can I get create new columns in a dataframe that will take care of spillover rows?如何在数据框中创建新列来处理溢出行?
【发布时间】:2021-05-06 01:59:36
【问题描述】:
我有一个看起来像这样的 pandas 数据框:
| Name |
Date |
Item1 |
Item2 |
| Andrew |
1/1/19 |
Apple |
Pear |
| Andrew |
1/1/19 |
Orange |
|
| John |
2/5/20 |
Banana |
|
| Steve |
2/3/21 |
Grape |
Apple |
我正在使用的数据框只有 Item1 和 Item2 列,因此如果一个人有第三个项目,则会为该人创建一个具有另一个 Item1 的新行。我想生成相同的 DataFrame,但是通过创建一个 Item3 列并将所有内容保持为每人一行。结果如下所示:
| Name |
Date |
Item1 |
Item2 |
Item3 |
| Andrew |
1/1/19 |
Apple |
Pear |
Orange |
| John |
2/5/20 |
Banana |
|
|
| Steve |
2/3/21 |
Grape |
Apple |
|
如何在 pandas 中实现这一点?请注意,每人最多可溢出 5 件物品,因此我最多需要 5 件物品。
【问题讨论】:
标签:
python
pandas
dataframe
pivot
【解决方案1】:
delimiter = '####'
dfn = (df.set_index(['Name','Date']).stack() # stack to drop NA Item
.reset_index(name='Item')
.groupby(['Name','Date'])['Item'].agg(delimiter.join) # for ('Name', 'Date') grouped, join item by delimiter
.str.split(delimiter, expand=True) # split string, and expand
.add_prefix('Item') # rename columns with prefix 'Item'
.reset_index()
)
print(dfn)
Name Date Item0 Item1 Item2
0 Andrew 1/1/19 Apple Pear Orange
1 John 2/5/20 Banana None None
2 Steve 2/3/21 Grape Apple None
方法2:
dfn = df.set_index(['Name','Date']).stack()
obj = dfn.groupby(level=[0,1]).agg(list)
df_result = (pd.DataFrame(obj.tolist(), index=obj.index)
.add_prefix('Item')
.reset_index())
print(obj)
Name Date
Andrew 1/1/19 [Apple, Pear, Orange]
John 2/5/20 [Banana]
Steve 2/3/21 [Grape, Apple]
dtype: object
print(df_result)
Name Date Item0 Item1 Item2
0 Andrew 1/1/19 Apple Pear Orange
1 John 2/5/20 Banana None None
2 Steve 2/3/21 Grape Apple None
【解决方案2】:
一种方法
df = pd.read_csv(io.StringIO("""Name Date Item1 Item2
Andrew 1/1/19 Apple Pear
Andrew 1/1/19 Orange
John 2/5/20 Banana
Steve 2/3/21 Grape Apple
"""), sep="\t")
# generate a column that is list of values
df = (df.groupby(["Name","Date"]).agg({c:lambda s: list(s.dropna()) for c in df.columns if "Item" in c}).reset_index()
.assign(tmp=lambda dfa: dfa.Item1 + dfa.Item2)
.drop(columns=[c for c in df.columns if "Item" in c])
)
# expand list into columns
df = df.loc[:,["Name","Date"]].join( df.tmp.apply(pd.Series).rename(columns={i:f"Item{i+1}" for i in range(5)}))
输出
Name Date Item1 Item2 Item3
Andrew 1/1/19 Apple Orange Pear
John 2/5/20 Banana NaN NaN
Steve 2/3/21 Grape Apple NaN