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

最后发布: 2018-01-31 07:32:47


问题

假设我有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 这似乎是一个问题。

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

python pandas loops dataframe
回答

输入

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(请参见此答案DataFrame Assign )应用于bar的所有元素(即, dfs.items()所有键/值对dfs.items()
  • dict()将地图转换为字典。

笔记

关于modify

请注意,您的函数modify未返回任何内容……我建议您对Python的可变性和不变性有更多了解。 这篇文章很有趣。

然后,您可以测试以下示例:

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() )-从这个答案

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

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

或者,如果df_1df_n已经存在,则自动df_1一些df_n -根据《 战地》第一部分的回答

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


回答

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

我只是在df_i = globals()["df_"+str(i)]添加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)


回答

您的代码示例使我有些困惑,但重点是

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

通常,我如何使用迭代器访问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)

现在,您可以通过循环遍历所有有效df来引用它们:

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])

或者,多一点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 alldfs = [var for var in dir() if isinstance(eval(var), pd.core.frame.DataFrame)] 这是易于复制粘贴的数据集和功能:

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

在此处输入图片说明

以下是操作后的数据框:

在此处输入图片说明

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

希望您会发现它有用!