以编程方式对Pandas数据帧进行切片

时间:2016-01-30 14:24:02

标签: python pandas dataframe

我有一堆数据帧,我试图切片并分配回原始名称。但我发现存在命名空间问题。以下是我所拥有的。

import pandas as pd
import numpy as np

df_a = pd.DataFrame(np.random.rand(14,2), columns = list('XY'))
df_b = pd.DataFrame(np.random.rand(14,2), columns = list('XY'))

mylist =[df_a, df_b]

def truncate_before(list_of_dfts, idx):
    for dfts in list_of_dfts:
        dfts = dfts[idx:]
        print(dfts.head)

truncate_before(mylist, 11)
print(df_a)

在truncate_before函数的print语句中,它显示3行,对应于第11行,第12行和第13行。但最终的print语句显示第0到第13行。

因此,在函数之外,它将恢复为原始数据帧。我的印象是Python通过引用传递参数。我错过了什么?

1 个答案:

答案 0 :(得分:1)

truncate_before

def truncate_before(list_of_dfts, idx):
    for dfts in list_of_dfts:
        dfts = dfts[idx:]
        print(dfts.head)

for-loop创建一个本地变量dfts,该变量引用list_of_dfts中的DataFrame。但是

        dfts = dfts[idx:]

将新值重新分配给dfts。它不会更改list_of_dfts中的DataFrame。

有关变量名称如何与值绑定的更好解释,请参阅Facts and myths about Python names and values,以及哪些操作更改值与将新值绑定到变量名称。

以下是一些替代方案:

修改地址列表

def truncate_before(list_of_dfts, idx):
    list_of_dfts[:] = [dfts[idx:] for dfts in list_of_dfts]
    for dfts in list_of_dfts:
        print(dfts.head)

因为分配到list_of_dfts[:](调用list_of_dfts.__setitem__)会更改list_of_dfts的内容。

import numpy as np
import pandas as pd

df_a = pd.DataFrame(np.random.rand(14,2), columns = list('XY'))
df_b = pd.DataFrame(np.random.rand(14,2), columns = list('XY'))

mylist = [df_a, df_b]

def truncate_before(list_of_dfts, idx):
    list_of_dfts[:] = [dfts[idx:] for dfts in list_of_dfts]

print(mylist[0])
truncate_before(mylist, 11)
print(mylist[0])

显示mylist[0]已被截断。请注意,df_a仍引用原始DataFrame。

返回列表并将mylistdf_a, df_b重新分配给结果

使用返回值可能无需就地修改mylist

要将全局变量df_adf_b 重新分配为新值,您可以制作 truncate_before返回DataFrame列表,并重新分配df_adf_b 返回值:

def truncate_before(list_of_dfts, idx):
    return [dfts[idx:] for dfts in list_of_dfts]

mylist = truncate_before(mylist, 11)   # or
# df_a, df_b = truncate_before(mylist, 11) # or
# mylist = df_a, df_b = truncate_before(mylist, 11)  

但请注意,通过mylistdf_a以及df_b访问DataFrame可能并不好,因为如上例所示,值不会自动保持协调。使用mylist就足够了。

将DataFrame方法与inplace参数一起使用,例如df.drop

dfts.dropinplace=True}修改dfts本身:

import numpy as np
import pandas as pd

df_a = pd.DataFrame(np.random.rand(14,2), columns = list('XY'))
df_b = pd.DataFrame(np.random.rand(14,2), columns = list('XY'))

mylist = [df_a, df_b]

def truncate_before(list_of_dfts, idx):
    for dfts in list_of_dfts:
        dfts.drop(dfts.index[:idx], inplace=True)

truncate_before(mylist, 11)
print(mylist[0])
print(df_a)

通过修改dfts inplace,mylist df_adf_b中的值 同时改变。

请注意,dfts.drop会根据索引标签值删除行。所以上面依赖 (假设)dfts.index是唯一的。如果dfts.index不是唯一的, dfts.drop行可能比idx行多。例如,

df = pd.DataFrame([1,2], index=['A', 'A'])
df.drop(['A'], inplace=True)

删除两个行,使df为空DataFrame。

另请注意Pandas核心开发人员关于使用inplace

的警告
  

我个人意见:我从不使用就地操作。语法更难     阅读并没有任何优势。

这可能是因为在幕后,dfts.drop创建了一个新的数据框架 然后调用_update_inplace私有方法将新数据分配给 旧的DataFrame:

def _update_inplace(self, result, verify_is_copy=True):
    """
    replace self internals with result.
    ...
    """
    self._reset_cache()
    self._clear_item_cache()
    self._data = getattr(result,'_data',result)
    self._maybe_update_cacher(verify_is_copy=verify_is_copy)

由于无论如何都必须创建临时result,因此“就地”操作与简单重新分配相比没有内存或性能优势。