如何根据 ID 将 Pandas 数据帧拆分为多个数据帧?

时间:2021-01-05 14:23:59

标签: python-3.x pandas dataframe

我在单个 csv 文件中有这些数据,其中有多个 ID、产品等标题。我想要每组的最后一个值(最后一行)。所有其他行都将被删除。有人可以帮我写一个脚本吗?数据如下所示:

<头>
ID 产品 SNF 蛋白质
365 PB 11.11.20 2016 年鲜奶监测 4.08 8.52 3.19
365 PB 11.11.20 2016 年鲜奶监测 4.04 8.52 3.2
365 PB 11.11.20 2016 年鲜奶监测 0.026 0.004 0.009
365 PB 11.11.20 2016 年鲜奶监测 4.06 8.52 3.2
ID 产品 SNF 蛋白质
465 PB 11.11.20 2016 年鲜奶监测 3.73 8.81 3.06
465 PB 11.11.20 2016 年鲜奶监测 3.72 8.8 3.08
465 PB 11.11.20 2016 年鲜奶监测 0.004 0.008 0.012
465 PB 11.11.20 2016 年鲜奶监测 3.73 8.81 3.07
ID 产品 SNF 蛋白质
1465 PB 11.11.20 2016 年鲜奶监测 4.08 8.52 3.15
1465 PB 11.11.20 2016 年鲜奶监测 4.04 8.52 3.16
1465 PB 11.11.20 2016 年鲜奶监测 0.026 0.004 0.006
1465 PB 11.11.20 2016 年鲜奶监测 4.06 8.52 3.15

我想得到的是这个,我的意思是每组的最后一行: |身份证|产品 |脂肪 | SNF |蛋白质 | |:---- |:------:| -----:| -----:|-----:| |365 PB 11.11.20 |鲜奶监测 2016 |4.06 |8.52 |3.2| |465 PB 11.11.20 |鲜奶监测 2016 |3.73 |8.81 |3.07| |1465 PB 11.11.20 |鲜奶监测 2016 |4.06 |8.52 |3.15|

谁能帮帮我?谢谢。

4 个答案:

答案 0 :(得分:3)

试试:

df.loc[df.eq(df.columns).all(1).shift(-1, fill_value=True)]

输出:

                  ID                     Product   Fat   SNF Protein
3    365 PB 11.11.20  Fresh Milk Monitoring 2016  4.06  8.52     3.2
8    465 PB 11.11.20  Fresh Milk Monitoring 2016  3.73  8.81    3.07
13  1465 PB 11.11.20  Fresh Milk Monitoring 2016  3.95  8.44    3.15

说明:代码可以这样分解:

meta_rows = df.eq(df.columns).all(1)

检查元行,即行中的所有单元格都等于标题。如果您的示例数据中的第一行不是列名,您可以使用:

meta_rows = df.eq(df.iloc[0]).all(1)

现在,您需要这些行之前的行,因此我们将元行标记向上移动:

marker = meta_rows.shift(-1, fill_value=True)

然后最后使用 bolean 索引来获取这些行:

df[marker]

答案 1 :(得分:2)

你可以分组和最后:

df = df.groupby(['ID'],as_index=False).last()
>>>df

ID                  Product                     Fat     SNF     Protein
365 PB 11.11.20     Fresh Milk Monitoring 2016  4.06    8.52    3.2
465 PB 11.11.20     Fresh Milk Monitoring 2016  3.73    8.81    3.07
1465 PB 11.11.20    Fresh Milk Monitoring 2016  3.95    8.44    3.15   

如果在该操作之后有不需要的行,则添加包含列名的内容:

df = df[df['ID'] !='ID']

更新 请注意,尽管这个解决方案看起来很简单,但它的性能比@Quang Hoang 的答案慢了 2 倍,所以它是可读性和性能的权衡...

我会选择可读性,因为对我来说 groupby 似乎更容易理解...但这取决于数据集的大小

答案 2 :(得分:0)

您可以先根据 ID 对 DataFrame 进行分组,然后遍历这些组

df_grp = df.groupby(by=['ID'])

 res = []
    
for group in df_grp:
    imm_df = group[1].iloc[[:, -1]] ### returns the last row
    res += [imm_df]
        
final_df = pd.concat(res,axis=0)
   

如果需要,您可以进一步更改 iloc range 以获取一系列行

答案 3 :(得分:0)

也许最好先将 CSV 文件拆分为单独的 CSV 文件,每个“块”数据一个,以便使用 pandas 加载每个文件变得微不足道。

这是使用 more_itertools 进行拆分的可能脚本:

import re
import more_itertools as mitt


HEADER_PATTERN = re.compile(r"^ID,Product,Fat,SNF,Protein$")


with open("data.csv") as file:
    lines = iter(file.readline, "")
    chunks = mitt.split_before(lines, HEADER_PATTERN.match)
    for i, chunk in enumerate(chunks):
        with open(f"data{i}.csv", "w") as output:
            output.writelines(chunk)

用实际文件名替换 "data.csv"。这会将每个块保存在文件 data0.csvdata1.csv 等中。

完成后,您可以简单地分别加载每个块并提取每个块的最后一行:

import itertools
import pandas as pd

# load each chunk
chunks = []
for i in itertools.count():
    try:
        chunk = pd.read_csv(f"data{i}.csv")
        chunks.append(chunk)
    except FileNotFoundError:
        break


# extract the last row of each
last_rows = pd.concat([df.iloc[-1:] for df in chunks])

那么:

>>> last_rows
               ID                     Product   Fat   SNF  Protein
365   PB 11.11.20  Fresh Milk Monitoring 2016  4.06  8.52     3.20
465   PB 11.11.20  Fresh Milk Monitoring 2016  3.73  8.81     3.07
1465  PB 11.11.20  Fresh Milk Monitoring 2016  3.95  8.44     3.15