【问题标题】:Iterating over different data frames using an iterator使用迭代器迭代不同的数据帧
【发布时间】:2018-07-10 05:54:58
【问题描述】:

假设我有n个数据帧df_1df_2df_3,...df_n,分别包含名为SPEED1SPEED2SPEED3,...的列SPEEDn,例如:

import numpy as np
df_1 = pd.DataFrame({'SPEED1':np.random.uniform(0,600,100)})
df_2 = pd.DataFrame({'SPEED2':np.random.uniform(0,600,100)})

我想对所有数据框进行相同的更改。我如何通过在类似的行上定义一个函数来做到这一点?

def modify(df,nr):
    df_invalid_nr=df_nr[df_nr['SPEED'+str(nr)]>500]
    df_valid_nr=~df_invalid_nr
    Invalid_cycles_nr=df[df_invalid]
    df=df[df_valid]
    print(Invalid_cycles_nr)
    print(df)

所以,当我尝试运行上述函数时

modify(df_1,1)

它返回整个数据帧而不进行修改,并将无效循环作为空数组返回。我猜我需要在函数的某处定义对全局数据框的修改才能使其正常工作。

我也不确定我是否可以用另一种方式做到这一点,比如在所有数据帧中循环一个迭代器。但是,我不确定它会起作用。

for i in range(1,n+1):
    df_invalid_i=df_i[df_i['SPEED'+str(i)]>500]
    df_valid_i=~df_invalid_i
    Invalid_cycles_i=df[df_invalid]
    df=df[df_valid]
    print(Invalid_cycles_i)
    print(df)

我一般如何使用迭代器访问df_1?这似乎是一个问题。

任何帮助将不胜感激,谢谢!

【问题讨论】:

  • 对于您的第二点,您可以在此处尝试解决方案stackoverflow.com/a/17960039/3941704。你能给出一个你期望的可重现的例子吗?
  • 我不确定你想做什么?我了解您要过滤所有df 以便一方面获得有效,另一方面获得无效?每个df 是否包含所有速度(即df1 具有speed1,..,speedn)或每个df 仅具有一种速度(即df1 具有speed1df2 具有speed2,等等) ?
  • 每个df只有一个速度。
  • 好的,那请看我的回答stackoverflow.com/a/48537995/3941704

标签: python pandas loops dataframe


【解决方案1】:

解决方案

输入

import pandas as pd
import numpy as np 

df_1 = pd.DataFrame({'SPEED1':np.random.uniform(1,600,100))
df_2 = pd.DataFrame({'SPEED2':np.random.uniform(1,600,100))

代码

在我看来,更好的方法是将您的 dfs 存储到一个列表中并枚举它以将信息扩充到您的 dfs 以创建一个 valid 列:

for idx, df in enumerate([df_1, df_2]):
    col = 'SPEED'+str(idx+1)
    df['valid'] = df[col] <= 500

print(df_1)

        SPEED  valid
0  516.395756  False
1   14.643694   True
2  478.085372   True
3  592.831029  False
4    1.431332   True

然后您可以使用df_1[df_1.valid]df_1[df_1.valid == False] 过滤有效或无效

这是适合您问题的解决方案,请参阅可能更简洁的另一种解决方案和下面的注释,了解您需要的解释。


另一种(更好的?)解决方案

如果您可以重新考虑您的代码。每个DataFrame都有一个列速,然后命名为SPEED

dfs = dict(df_1=pd.DataFrame({'SPEED':np.random.uniform(0,600,100)}),
           df_2=pd.DataFrame({'SPEED':np.random.uniform(0,600,100)}))

它将允许您执行以下一条线:

dfs = dict(map(lambda key_val: (key_val[0],
                                key_val[1].assign(valid = key_val[1]['SPEED'] <= 500)),
               dfs.items()))

print(dfs['df_1'])

        SPEED  valid
0  516.395756  False
1   14.643694   True
2  478.085372   True
3  592.831029  False
4    1.431332   True

说明:

  • dfs.items() 返回键(即名称)和值(即 DataFrame)的列表
  • map(foo, bar) 将函数 foo(参见 this answerDataFrame assign)应用于 bar 的所有元素(即应用于 dfs.items() 的所有键/值对。
  • dict() 将地图转换为字典。

注意事项

关于modify

请注意,您的函数 modify 没有返回任何内容...我建议您阅读更多关于 Python 中的可变性和不变性的内容。这个article很有意思。

然后您可以测试以下内容:

def modify(df):
    df=df[df.SPEED1<0.5]
    #The change in df is on the scope of the function only, 
    #it will not modify your input, return the df...
    return df

#... and affect the output to apply changes
df_1 = modify(df_1)

关于使用迭代器访问df_1

请注意,当您这样做时:

for i in range(1,n+1):
    df_i something

df_i 在您的循环中将为每次迭代调用对象df_i(而不是df_1 等) 要按名称调用对象,请改用globals()['df_'+str(i)](假设df_1df_n+1 位于globals()) - 来自此answer

在我看来,这不是一个干净的方法。我不知道你如何创建你的 DataFrames,但如果可能的话,我建议你将它们存储到字典中,而不是手动影响:

dfs = {}
dfs['df_1'] = ...

如果df_1df_n 已经存在,或者更自动一些 - 根据vestland answer 的第一部分:

dfs = dict((var, eval(var)) for
           var in dir() if
           isinstance(eval(var), pd.core.frame.DataFrame) and 'df_' in var)

然后你会更容易迭代你的 DataFrame:

for i in range(1,n+1):
    dfs['df_'+str(i)'] something

【讨论】:

    【解决方案2】:

    您可以使用globals() 函数,该函数允许您通过他的名字获取变量。

    我只是在 for 循环的开头添加df_i = globals()["df_"+str(i)]

    for i in range(1,n+1):
        df_i = globals()["df_"+str(i)]
        df_invalid_i=df_i.loc[df_i['SPEED'+str(i)]>500]
        df_valid_i=~df_invalid_i
        Invalid_cycles_i=df[df_invalid]
        df=df[df_valid]
        print(Invalid_cycles_i)
        print(df)
    

    【讨论】:

      【解决方案3】:

      你的代码示例让我有点困惑,但重点是

      我想对所有数据框进行相同的更改。

      一般来说,我如何使用迭代器访问 df_1?

      您可以通过在字典 (dict) 中组织数据帧 (dfs) 来做到这一点。

      方法如下:


      假设你的命名空间中有一堆变量......

      # Imports
      import pandas as pd
      import numpy as np
      
      # A few dataframes with random numbers
      # df_1
      np.random.seed(123)
      rows = 12
      rng = pd.date_range('1/1/2017', periods=rows, freq='D')
      df_1 = pd.DataFrame(np.random.randint(100,150,size=(rows, 2)), columns=['a', 'b']) 
      df_1 = df_1.set_index(rng)
      
      # df_2
      np.random.seed(456)
      rows = 12
      rng = pd.date_range('1/1/2017', periods=rows, freq='D')
      df_2 = pd.DataFrame(np.random.randint(100,150,size=(rows, 2)), columns=['c', 'd']) 
      df_2 = df_2.set_index(rng)
      
      # df_3
      np.random.seed(789)
      rows = 12
      rng = pd.date_range('1/1/2017', periods=rows, freq='D')
      df_3 = pd.DataFrame(np.random.randint(100,150,size=(rows, 2)), columns=['e', 'f']) 
      df_3 = df_3.set_index(rng)
      

      ...您可以使用以下方法识别所有数据框:

      alldfs = [var for var in dir() if isinstance(eval(var), pd.core.frame.DataFrame)]
      

      如果您有很多不同的数据帧,但只想关注那些前缀为“df_”的数据帧,您可以通过...来识别它们

      dfNames = []
      for elem in alldfs:
         if str(elem)[:3] == 'df_':
             dfNames.append(elem)
      

      ...然后使用以下命令将它们组织在一个字典中:

      myFrames = {}
      for dfName in dfNames:
          myFrames[dfName] = eval(dfName)
      

      从有趣的数据框列表中,您可以对那些您想使用的数据框进行子集化。以下是您只关注 df_1 和 df_2 的方法:

      invalid = ['df_3']
      for inv in invalid:
          myFrames.pop(inv, None)
      

      现在您可以通过循环引用所有有效的 dfs:

      for key in myFrames.keys():
          print(myFrames[key])
      

      这应该涵盖...

      一般来说,我如何使用迭代器访问 df_1?

      ...问题的一部分。

      您当然可以通过字典中的名称/键来引用单个数据框:

      print(myFrames['df_1'])
      

      从这里您可以对所有数据框中的所有列进行操作。

      for key in myFrames.keys():
          myFrames[key] = myFrames[key]*10
          print(myFrames[key])
      

      或者,更 Python 一点,您可以指定一个 lambda 函数并将其应用于列的子集

      # A function
      decimator = lambda x: x/10
      
      # A subset of columns:
      myCols = ['SPEED1', 'SPEED2']
      

      将该函数应用于您感兴趣的数据框中的列子集:

      for key in myFrames.keys():
          for col in list(myFrames[key]):
              if col in myCols:
                  myFrames[key][col] = myFrames[key][col].apply(decimator)
                  print(myFrames[key][col])
      

      那么,回到你的功能...

      修改(df_1,1)

      ...这是我对它的看法,它包含在一个函数中。

      首先我们将重新定义数据框和函数。 哦,使用此设置,您将必须使用alldfs = [var for var in dir() if isinstance(eval(var), pd.core.frame.DataFrame)] 获取函数之外的所有 dfs。 这是用于轻松复制粘贴的数据集和函数:

      # Imports
      import pandas as pd
      import numpy as np
      
      # A few dataframes with random numbers
      # df_1
      np.random.seed(123)
      rows = 12
      rng = pd.date_range('1/1/2017', periods=rows, freq='D')
      df_1 = pd.DataFrame(np.random.randint(100,150,size=(rows, 3)), columns=['SPEED1', 'SPEED2', 'SPEED3']) 
      df_1 = df_1.set_index(rng)
      
      # df_2
      np.random.seed(456)
      rows = 12
      rng = pd.date_range('1/1/2017', periods=rows, freq='D')
      df_2 = pd.DataFrame(np.random.randint(100,150,size=(rows, 3)), columns=['SPEED1', 'SPEED2', 'SPEED3']) 
      df_2 = df_2.set_index(rng)
      
      # df_3
      np.random.seed(789)
      rows = 12
      rng = pd.date_range('1/1/2017', periods=rows, freq='D')
      df_3 = pd.DataFrame(np.random.randint(100,150,size=(rows, 3)), columns=['SPEED1', 'SPEED2', 'SPEED3']) 
      df_3 = df_3.set_index(rng)
      
      # A function that divides columns by 10
      decimator = lambda x: x/10
      
      # A reference to all available dataframes
      alldfs = [var for var in dir() if isinstance(eval(var), pd.core.frame.DataFrame)]
      
      # A function as per your request
      def modify(dfs, cols, fx):
      
          """ Define a subset of available dataframes and list of interesting columns, and
              apply a function on those columns.
          """
      
      
          # Subset all dataframes with names that start with df_
          dfNames = []
          for elem in alldfs:
             if str(elem)[:3] == 'df_':
                 dfNames.append(elem)
      
          # Organize those dfs in a dict if they match the dataframe names of interest
          myFrames = {}
          for dfName in dfNames:
              if dfName in dfs:    
                  myFrames[dfName] = eval(dfName)
                  print(myFrames)
      
          # Apply fx to the cols of your dfs subset
          for key in myFrames.keys():
              for col in list(myFrames[key]):
                  if col in cols:
                      myFrames[key][col] = myFrames[key][col].apply(decimator)
      
      # A testrun. Results in screenshots below
      modify(dfs = ['df_1', 'df_2'], cols = ['SPEED1', 'SPEED2'], fx = decimator)
      

      以下是操作前的数据帧 df_1 和 df_2:

      以下是操作后的数据框:

      无论如何,这就是我的处理方式。

      希望你会发现它有用!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-09
        • 2020-10-11
        • 1970-01-01
        相关资源
        最近更新 更多