Python 如何将.loc与groupby一起使用,以便基于分组数据创建新列;你不认为是副本吗?

Python 如何将.loc与groupby一起使用,以便基于分组数据创建新列;你不认为是副本吗?,python,pandas,dataframe,pandas-groupby,Python,Pandas,Dataframe,Pandas Groupby,我有一个包含多组数据的CSV文件,正在使用groupby()方法将它们分离。每个组都经过一点简单的数学处理,包括对两列使用min()和max(),以及一点减法和乘法来创建新的数据列。然后,我绘制每组的图表。这基本上是可行的,但我对我的代码图有两个抱怨——它们是单独的,而不是我喜欢的组合;我在每个组中都会看到“SettingWithCopyWarning”。从我的搜索中,我相信解决方案要么是使用.loc,要么是使用更好的拆分应用(并可能结合)方法。 我可以在Excel中完成这项工作,但我正在尝试学

我有一个包含多组数据的CSV文件,正在使用
groupby()
方法将它们分离。每个组都经过一点简单的数学处理,包括对两列使用
min()
max()
,以及一点减法和乘法来创建新的数据列。然后,我绘制每组的图表。这基本上是可行的,但我对我的代码图有两个抱怨——它们是单独的,而不是我喜欢的组合;我在每个组中都会看到“SettingWithCopyWarning”。从我的搜索中,我相信解决方案要么是使用
.loc
,要么是使用更好的拆分应用(并可能结合)方法。 我可以在Excel中完成这项工作,但我正在尝试学习Python,并且,在我的代码运行时,我希望改进它

import os.path
import sys
import pandas as pd

filename = "data/cal_data.csv"
df = pd.read_csv(filename, header=0) #one line of headers

df['Test']="Model "+df['Model No'] +", SN "+ df['Serial No'].values.astype(str) +", Test time "+ df['Test time'].values.astype(str) # combining several columns into a single column that makes grouping straight-forward, and simplifies titles of graphs. Not completely necessary.

df = df[df.index <= df.groupby('Test')['Test Point'].transform('idxmax')]#drop rows after each max test point

for title, group in df.groupby('Test'):
    x1, x2 = min(group["Test Reading"]),max(group["Test Reading"])
    x4, x3 = max(group["Test Point"]),min(group["Test Point"]) #min is usually zero
    R=(x2-x1)/(x4-x3) #linearize
    
    group['Test Point Error']=100*(group['Test Reading']- (group['Test Point']*R+x1))
    
    ax=group.plot(x='Test Point', y='Test Point Error', title=title, grid=True)
    ax.set_ylabel("% error (+/-"+str(Error_Limit)+"% limit)")
编辑-添加了来自
df.head(20)
的输出,以及一对绘图的图像:

 Test Point Test Reading    Test
0   0   0.10453 Model LC-500, SN 937618, Test time 17:20:10
1   20  0.17271 Model LC-500, SN 937618, Test time 17:20:10
2   50  0.27838 Model LC-500, SN 937618, Test time 17:20:10
3   100 0.45596 Model LC-500, SN 937618, Test time 17:20:10
4   150 0.63435 Model LC-500, SN 937618, Test time 17:20:10
5   200 0.81323 Model LC-500, SN 937618, Test time 17:20:10
6   250 0.99252 Model LC-500, SN 937618, Test time 17:20:10
7   300 1.17222 Model LC-500, SN 937618, Test time 17:20:10
8   350 1.35219 Model LC-500, SN 937618, Test time 17:20:10
9   400 1.53260 Model LC-500, SN 937618, Test time 17:20:10
10  450 1.71312 Model LC-500, SN 937618, Test time 17:20:10
11  500 1.89382 Model LC-500, SN 937618, Test time 17:20:10
14  0   0.10468 Model LC-500, SN 937618, Test time 17:31:46
15  20  0.17284 Model LC-500, SN 937618, Test time 17:31:46
16  50  0.27856 Model LC-500, SN 937618, Test time 17:31:46
17  100 0.45609 Model LC-500, SN 937618, Test time 17:31:46
18  150 0.63457 Model LC-500, SN 937618, Test time 17:31:46
19  200 0.81341 Model LC-500, SN 937618, Test time 17:31:46
20  250 0.99277 Model LC-500, SN 937618, Test time 17:31:46
21  300 1.17237 Model LC-500, SN 937618, Test time 17:31:46

编辑/更新2020年7月23日: 我做了一些变通办法,使这项工作,但我仍然感谢任何帮助。下面是修改后的for循环代码,将每个组写入一个新的csv文件以供以后读取(这样我可以添加在此处创建的新列),如果临时文件已经存在,还可以删除它:

if os.path.exists("data/temp.csv"):
    os.remove("data/temp.csv")
for title, group in df.groupby('Test'):

    x1 = min(group["Test Reading"].head(1))
    x2 = max(group["Test Reading"].tail(1))
    x3 = min(group["Test Point"].head(1))
    x4 = max(group["Test Point"].tail(1))
    R=(x2-x1)/(x4-x3) #linearization scalar
    group['Test Point Error'] =100*(group['Test Reading']- (group['Test Point']*R+x1))/(x2-x1)
    file = open('data/temp.csv','a')
    group.to_csv('data/temp.csv', mode="a", index=False, columns=columns, header=False)#, header=True, index=True, index_label=None, mode='w', encoding=None, compression='infer', quoting=None, quotechar='"', line_terminator=None, chunksize=None, date_format=None, doublequote=True, escapechar=None, decimal='.'))
    file.close()
然后,读取临时csv,我使用seaborn(
import seaborn as sns
import matplotlib.pyplot as plt
将多个组绘制在一起,按序列号分组,每行4个子批

df = pd.read_csv('data/temp.csv', header=0)
df['Model/SN']=df['Model No']+" / "+df['Serial No'].values.astype(str)
g = sns.FacetGrid(df, col='Model/SN', hue='Test', col_wrap=4, sharey=False, sharex=False)

g.map(plt.axhline, y=Error_Limit, ls='--', c='red')
g.map(plt.axhline, y=-Error_Limit, ls='--', c='red')

g = g.map(sns.lineplot, 'Test Point', 'Test Point Error', ci=None)

总结-这些修复并不理想;它们是解决方案,我仍然收到“SettingWithCopyWarning”错误。

因此您要求:

  • 如何停止将值设置为副本
  • 如何为matplotlib中的每个组创建包含子图的图
  • “SettingWithCopyWarning”之所以出现,是因为您正在为每个组创建一列并设置值,而每个组本身就是数据帧某些行的副本。我不在每个循环上设置值,而是将“Test\u Point\u Error”存储在系列列表中,并在退出for循环后,将其添加到DF中

    ---编辑--- 尝试替换:

    group['Test Point Error']=100*(group['Test Reading']- (group['Test Point']*R+x1))
    

    这将为每个组添加一个序列,索引与df.index匹配。完成后,df中的每一行将有一行错误。因此,退出for循环后:

    df.assign(test_point_error=pd.concat(error_list))
    
    将完全匹配每一行,而不考虑df上的任何排序

    ---编辑结束---

    子绘图问题与此类似,您在循环时分别绘制每个组。如果您在退出for loop后绘制,则

    df.groupby().plot(subplots=True)
    
    我会给你想要的

    在另一个主题中,我将取消“Test”的字符串连接,并执行以下操作:

    df.groupby(['Model No', 'Serial No', 'Test Time'])
    
    如果有许多行,这可能会使您的代码更快。

    因此您要求:

  • 如何停止将值设置为副本
  • 如何为matplotlib中的每个组创建包含子图的图
  • “SettingWithCopyWarning”之所以出现,是因为您正在为每个组创建一列并设置值,而每个组本身就是数据帧某些行的副本。我不在每个循环上设置值,而是将“Test\u Point\u Error”存储在系列列表中,并在退出for循环后,将其添加到DF中

    ---编辑--- 尝试替换:

    group['Test Point Error']=100*(group['Test Reading']- (group['Test Point']*R+x1))
    

    这将为每个组添加一个序列,索引与df.index匹配。完成后,df中的每一行将有一行错误。因此,退出for循环后:

    df.assign(test_point_error=pd.concat(error_list))
    
    将完全匹配每一行,而不考虑df上的任何排序

    ---编辑结束---

    子绘图问题与此类似,您在循环时分别绘制每个组。如果您在退出for loop后绘制,则

    df.groupby().plot(subplots=True)
    
    我会给你想要的

    在另一个主题中,我将取消“Test”的字符串连接,并执行以下操作:

    df.groupby(['Model No', 'Serial No', 'Test Time'])
    

    如果有许多行,这可能会使您的代码更快。

    谢谢您的建议。我不确定创建序列列表的过程是什么样子。我尝试了字符串串联替换-它确实运行得更快,但每个序列号除了一个组外,我失去了所有组。也许我需要获得您对子批次工作的建议首先是ing(使用系列列表,我不确定如何将每个新系列正确地附加到该列表中…)谢谢你的建议。我不确定创建序列列表是什么样子。我尝试了字符串串联替换-它确实运行得更快,但每个序列号除了一个组外,我丢失了所有组。也许我需要先得到你关于子批次的建议(带有系列列表,我不知道如何正确地将每个新系列附加到此列表中…)