【问题标题】:How to iterate over the rows from 2 files, compare the values and update the value in a file when the condition is met?如何遍历 2 个文件中的行,比较值并在满足条件时更新文件中的值?
【发布时间】:2021-11-26 15:51:45
【问题描述】:

为了将值从 10 更改为 18、19 或 20,我正在拆分字符串,访问子字符串,然后尝试更改它。它的工作,但只是不改变价值观。这是我正在尝试实施的解决方案:

oldFileName = 'tryout.hmo'
newFileName = 'tryout_NEW.hmo'
topoFileName = 'Density.topo'

readme = open( oldFileName, "r" )
oldLines = readme.readlines()       

readme = open(topoFileName, "r")
Lines = readme.readlines()
readme.close()

newFile = open(newFileName,"w")


for row in oldLines:
    for line in Lines:
        tmp = line.split()
    list =  row.rstrip()
    tmp1 = list.split()
    newFile.write(row)
    if row.find("BEG_ELEM_DATA") > -1:
       if tmp[0] == tmp1[0]:
           if tmp[2] == 1 and tmp[3] == 0:
            # it is magnet, value 18
             newFile.write(tmp1.replace(tmp1[1], "18"))
         
           elif tmp[2] == 1 and tmp[3] == 1:               
              # it is iron, value 19
             newFile.write(tmp1.replace(tmp1[1], "19"))
         
           else:
            # it is air, value 20
             newFile.write(tmp1.replace(tmp1[1], "20")) 

newFile.close()      

如果你能在上面的脚本中解决这个问题,我将不胜感激,那么我想它应该可以工作。

【问题讨论】:

    标签: python pandas dataframe loops csv


    【解决方案1】:

    最后,这是您正在寻找的解决方案。

    import pandas as pd
    df2 = pd.read_csv('Density.topo', header = 0, names = list('ABCD'), delimiter=r'\s+', skiprows=1)
    df2[['C', 'D']]= df2[['C', 'D']].round()
    
    new_file_content=''
    
    with open('tryout.hmo', 'r') as f:
        for line in f:
    
            if line[11:13] == '10':
    
                if line[3].isspace():
                    ID_to_search_for = line[4:8] # number with 4 digits
                else:
                    ID_to_search_for = line[3:8] # number with 5 digits
                search_idx = df2[df2['A'] == ID_to_search_for].index[0]
    
                if df2['C'][search_idx] == 1 and df2['D'][search_idx] == 0:
                    change = '18' #magnet
                    new_line = line[:11] + change + line[13:]
                elif df2['C'][search_idx] == 1 and df2['D'][search_idx] == 1:
                    change = '19' #iron
                    new_line = line[:11] + change + line[13:]
                else:
                    change = '20' #air
                    new_line = line[:11] + change + line[13:]
    
                new_file_content += new_line
    
            else:
                new_file_content += line
        
    with open('tryout_changed.hmo', 'w') as f:
        f.write(new_file_content)
    

    如果你不想使用数据框,你可以这样做:

    with open('density.topo') as f:
        lists_of_list = [line.rstrip().split() for line in f]
    
    new_file_content=''
    with open('tryout_test.hmo', 'r') as f:
        
        for line in f:
            
            if line[11:13] == '10':
                if line[3].isspace():
                    ID_to_search_for = line[4:8] # number with 4 digits
                else:
                    ID_to_search_for = line[3:8] # number with 5 digits
                for idx, sublist in enumerate(lists_of_list):
                    if sublist[0] == ID_to_search_for:                    
                        if lists_of_list[idx][2] == 1 and lists_of_list[idx][3] == 0:
                            change = '18' #magnet
                            new_line = line[:11] + change + line[13:]
                        elif lists_of_list[idx][2] == 1 and lists_of_list[idx][3] == 1:
                            change = '19' #iron
                            new_line = line[:11] + change + line[13:]
                        else:
                            change = '20' #air
                            new_line = line[:11] + change + line[13:]
    
                        new_file_content += new_line
    
            else:
                new_file_content += line
        
    with open('tryout_changed.hmo', 'w') as f:
        f.write(new_file_content)
    

    好的,这是我的最终答案。它(再次)执行您正在搜索的所有内容。如果有问题,请在您的 IDE 中调试您的代码。您应该开始使用上下文管理器,而不是逐步打开和关闭文件。 我在问题中围绕您的代码编写了新代码,并向其中添加了一些 cmets。

    oldFileName = 'tryout.hmo'
    newFileName = 'tryout_NEW.hmo'
    topoFileName = 'Density.topo'
    
    readme = open( oldFileName, "r" )
    oldLines = readme.readlines()
    
    m = int(oldLines[3])
    print(m)
    new_m = m+3
    m1 = str(m)
    new_m1 = str(new_m)
    
    Phrase = "END_COMP_DATA"
    #n = "Phrase not found" #not used --> not needed
    
    with open(oldFileName,"r") as oldFile: 
        for number, lin in enumerate(oldFile):
            if Phrase in lin:
              n = number
           
    #insert 3 lines to tryout_new at the right position (--> row n)
    magnet = f"      {m+1} "'"                     topo_magnet"'"\n" 
    iron   = f"      {m+2} "'"                       topo_iron"'"\n" 
    air    = f"      {m+3} "'"                        topo_air"'"\n"  
        
    oldLines[n:n] = [magnet, iron, air]
    
    newFile = open(newFileName,"w") 
    flag = 0
    
    with open('density.topo') as f:
        data_density = [line.rstrip().split() for line in f]
    
    for idx, row in enumerate(oldLines):
        lst =  row.rstrip() #I think you shouldn't name a variable like a class in python (list). use 'lst' or something like that
        tmp_tryout = lst.split()
        if row.find("BEG_ELEM_DATA") > -1:
            flag = 1
        if flag == 1 and len(tmp_tryout)>1:
            # if the column has more than 2 columns (after split), check for the "10"
            if tmp_tryout[1] == '10':
                # density_idx_line searchs in density.topo for a match with tmp_tryout[0] (e.g. 3749) and stores the whole line
                density_idx_line = list(filter(lambda x: x[0] == tmp_tryout[0], data_density))
                if len(density_idx_line) >0:
                    if density_idx_line[0][2] == '1.0' and density_idx_line[0][3] == '1e-05':
                        # the ' 10 ' is the 10 with a whitespace before and after it. Only like this only the 10 gets replaced (and not e.g. 3104 to 3184)    
                        newFile.write(row.replace(' 10 ', ' 18 '))       
                    elif density_idx_line[0][2] == '1.0' and density_idx_line[0][3] == '1.0':               
    
                        newFile.write(row.replace(' 10 ', ' 19 ')) 
                    else:
    
                        newFile.write(row.replace(' 10 ', ' 20 '))
            else: 
                newFile.write(row)
        else: 
            if idx == 3: 
                newFile.write(row.replace(m1, new_m1)) 
            else:
                newFile.write(row)       
                    
    newFile.close()
    print ("script terminated successfully!")
    

    【讨论】:

    • 此代码将有效,但仅适用于此特定文件而不适用于其他文件,很可能我在 python 中搜索解决方案而不使用数据框。我在 python 中尝试了一种解决方案,它几乎可以工作但没有更新值,我会在那里标记你。
    • 其他文件是什么意思?这个文件的格式很乱,所以我只是为这个特殊的文件编写了代码。如果您想更一般地使用它,您可以使用正则表达式来查找所有这些空格之间的不同“列”,这些空格一直在变化。不使用数据框也不应该是一个大问题,只是比较第二个文件中的值的不同方式(因为对我来说更困难的问题是在试用文件中查找和更改值)。是的,如果您让我知道您的最终解决方案是什么样的,我将不胜感激。
    • 是的,我在更改试用文件中的值时也面临同样的问题。我将编辑我的问题并将代码放在我上面的问题中。如果你能找到这方面的灵魂,我将不胜感激,然后它可以用于所有其他文本文件。
    • 我已经在没有问题的答案解决方案部分添加了代码。请在上面检查。此外,在不使用数据框的代码中,仅将值从 10 更改为 20。
    • 我会尽力的。我将在答案中查看并调试您的脚本。您使用什么 IDE,或者您在哪里编写代码?你为什么不使用我的建议?如果处处为 20,则 if/elif 条件永远不会正确。你也可以在你的地方调试我的代码为什么它不工作......对我来说它工作。
    【解决方案2】:

    好的,这是另一个解决方案。对于阅读本文的其他人:这仍然只是一个临时解决方案,但@Sagar 和我都不知道如何做得更好。

    import pandas as pd
    df = pd.read_csv('tryout.hmo', header = 0, names = list('ABCDEFGHIJKLM'), delimiter=r'\s+', skiprows=[i for i in range(52362)])
    df2 = pd.read_csv('Density.topo', header = 0, names = list('ANOP'), delimiter=r'\s+', skiprows=1)
    df2 = df2.iloc[:-3, :]
    
    df3 = df.merge(df2, how='outer', on='A')
    df3[['O','P']] = df3[['O','P']].fillna(-1).astype(int).replace(-1, np.nan)
    
    df3['B']= df3.apply(lambda x: 18 if x['B']==10 and x['O']==1 and x['P']==0 else (
        19 if x['B']==10 and x['O']==1 and x['P']==1 else (
        20 if x['B']==10 and x['O']==0 and x['P']==0 else x['B'])), axis=1)
    
    df3.to_csv('new_tryout.csv')
    

    它在不到一秒的时间内完成了代码,因此它比 iterrows 或 itertuples 好得多。 新的 csv 文件包括试用文件和密度文件。它们由试用文件的第一列合并在一起(我猜是 ID)

    我没有检查所有这些非常大的文件,但从我检查的几个随机点来看,似乎这种方式有效。

    【讨论】:

    • 我检查了它,但是如果您检查了诸如 3749 之类的元素或任何在试用文件中具有值 10 的元素没有更改,则该文件也没有正确合并。它很难使用合并文件,因为我希望它在具有更改值的同一个试用文件中。
    • 是的,试用文件根本没有改变。它是关于 new_tryout 文件的,我刚刚检查了 3749。它的值应该是 18。在将其保存为 new_tryout 文件之前,您可以删除最后 3 列,这样您就只有原始的试用列,没什么大不了的
    • 是的,我的意思是在new_tryout文件中,3749行如下:2765,3749,10.0,106.0,10690.0,10692.0,10693.0,10695.0,10658.0,10689.0,,,,,2, 1.0,0.0.,所以 10 的值在我的 csv 中没有改变。此外,正如您所说,new_tryout 文件需要删除最后三列和第一列,并添加试用文件的其余部分,然后它将是更改值的试用文件。我猜这变得更复杂了,你怎么看?
    • 很奇怪。您是否完全按照我发布的方式复制粘贴了我的代码?也许你有不同的python版本。对于您问题的第二部分:删除列很容易。由于列不一致,添加试用文件的第一部分可能有点棘手。我会尽力。不过,这对我来说总是很好的做法。
    • 我使用的是 3.9.5 python 版本。
    【解决方案3】:

    我还是 Python 的初学者,但我试图解决您的问题,这是我的解决方案:

    我想有更好的方法可以做到这一点,因为您必须在比较之前将所有数据导入数据框。

    另外我不知道您是否可以使用 pd.read_csv 将数据读取到数据框,因为我不知道 *.hmo 和 *.topo

    import pandas as pd
    
    df = pd.read_csv('tryout.csv', delimiter=';')
    df2 = pd.read_csv('density.csv', delimiter=';')
    
    for idx, row in df.iterrows():
        for idx2, row2 in df2.iterrows():
            if row[0] == row2[0]:
                if row2[2] == 1 and row2[3] == 0 :
                    # it is magnet, value 18
                    row[1] = 18
                elif row2[2] == 1 and row2[3] == 1 :
                    # it is iron, value 19
                    row[1] = 19
                else:
                    # it is air, value 20
                    row[1] = 20
    df.to_csv('new_tryout.csv')
    

    我的代码在这里做什么,它将两个文件都加载到数据帧中。然后遍历每一行以比较两个文件中的 ID 相同的位置(例如 3749)。 如果为真,则有 3 个 if 语句是否是磁铁/铁/空气,并将 df 中的值更改为正确的数字。

    最后将新的df保存到一个新文件'new_tryout.csv'

    我为它创建了 2 个测试文件,它按应有的方式工作。

    【讨论】:

    • 感谢您的回复。它在数据框中很容易,但主要问题是试用文件很难转换为 csv,因为它具有不同大小的行和列,这在我上面解释的整个试用文件中有所不同,我不确定元素列是行 [0]这是第一列(如果它在数据框中,但我猜不是在这里)这就是为什么我尝试使用 python 并且仅在试用文件中的 BEG_ELEM_DATA 中需要从 10 更改为 18 或 19 或 20。如果你愿意,我可以分享我的文件,但这里没有分享选项,是吗?
    • 您知道 BEG_ELEM_DATA 是否具有相同的列大小吗?然后您可以跳过 BEG_ELEM_DATA 开始之前的所有行,然后按照建议将其读入
    • 我尝试了使用 pd.read_csv(r'path\file name.txt) 和 to_csv 方法进行转换的常用方法,但问题是这些值不在单独的列中:对于密度文件- 一列中的所有值,如 3749 10 1.0 0.0 和试用文件中的所有值。你知道如何解决这个问题吗?
    • 不知道有没有直接分享数据的方法。您可以将其上传到某处并在此处复制链接。
    • 如果我理解正确,您只需将分隔符设置为制表符 (delimiter='\t')
    猜你喜欢
    • 1970-01-01
    • 2019-02-03
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    • 1970-01-01
    • 2023-01-31
    • 1970-01-01
    • 2017-01-27
    相关资源
    最近更新 更多