使用迭代器迭代不同的数据帧

时间:2018-01-31 07:32:48

标签: python pandas loops dataframe

假设我有n个数据框{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "attach", "name": "Attach", "port": 9229 }, { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceRoot}/make", "args": [ "run" ] } ] } df_1df_2,... df_3,分别包含名为df_n,{{的列1}},SPEED1,...,SPEED2,例如:

SPEED3

我希望对所有数据框进行相同的更改。如何通过在类似的行上定义函数来实现?

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) ?这似乎是一个问题。

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

3 个答案:

答案 0 :(得分:2)

解决方案

输入

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()返回键(即名称)和值列表(即DataFrames)
  • map(foo, bar)将函数foo(请参阅this answerDataFrame assign)应用于bar的所有元素(即dfs.items()的所有键/值对。< / LI>
  • 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

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

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)

然后,您可以更轻松地迭代DataFrames:

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

答案 1 :(得分:1)

您可以使用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)

答案 2 :(得分:1)

你的代码示例让我有些困惑,但专注于

  

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

  

一般来说,如何使用迭代器访问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)]

如果你有很多不同的数据框,但只想关注那些带有前缀的文章,那么你可以通过...来识别这些数据框。

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

...然后使用以下命令在dict中组织它们:

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?

......问题的一部分。

当然,您可以通过dict中的名称/键引用单个数据帧:

print(myFrames['df_1'])

从这里,您可以对所有数据框中的所有列执行某些操作。

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

或者,更加pythonic,你可以指定一个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 OUTSIDE您的功能。 这是数据集和简单复制粘贴的功能:

# 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:

enter image description here

以下是操作后的数据框:

enter image description here

无论如何,这就是我接近它的方式。

希望你能发现它有用!