python中的concat疑惑

2021-02-08 10:25发布

data=[[df],[df],[df]]     #一个列表中有3个DataFrame数据df=pd.concat(data[0])df=pd.concat(data[1])df=pd.concat...

data=[[df],[df],[df]]     #一个列表中有3个DataFrame数据

df=pd.concat(data[0])

df=pd.concat(data[1])

df=pd.concat(data[2])

这三句如何理解?结果是将列表分成3个df数据,但concat不是合并吗?


2条回答
007
2楼 · 2021-02-25 14:53





有关Python内编写类的各种技巧和方法(构建和初始化、重载操作符、类描述、属性访问控制、自定义序列、反射机制、可调用对象、上下文管理、构建描述符对象、Pickling)。你可以把它当作一个教程,进阶,或者使用参考;我希望它能够成为一份针对Python方法的用户友好指南。

内容目录

介绍

构建和初始化

使操作符在自定义类内工作

神奇方法——比较

神奇方法——数字

描述你的类

属性访问控制

制作自定义序列

反射

可调用对象

上下文管理

构建描述符对象

Pickling你的对象


总结

附录:如何调用神奇方法


  1. 介绍

  2. 这份指南是几个月内最有价值的Blog投稿精华。它的主题是向大家讲述Python中的神奇方法。

  3. 何为神奇方法呢?它们是面向Python中的一切,是一些特殊的方法允许在自己的定义类中定义增加“神奇”的功能。它们总是使用双下划线(比如__init__或__lt__),但它们的文档没有很好地把它们表现出来。所有这些神奇方法都出现在Python的官方文档中,但内容相对分散,组织结构也显得松散。还有你会难以发现一个实例(虽然他们被设计很棒,在语言参考中被详细描述,可之后就会伴随着枯燥的语法描述等)。

  4. 所以,为了解决我认为在Python文档中的一大败笔,我打算用更多纯英语,实例驱动的文档来说明Python的神奇方法。然后我就开始花了几周的时间来写blog,而现在我已经完成了它们,并将它们合订成一份指南。

  5. 我希望你喜欢它。把它当作一个教程,进阶,或者使用参考;我希望它能够成为一份针对Python方法的用户友好指南。

  6. 2.构建和初始化

  7. 相信大家都熟悉这个最基础的神奇方法__init__。它令你能自定义一个对象的初始化行为。而当我调用x=SomeClass()时,init并不是最先被调用的。实际上有一个叫做new的方法,事实上是它创建了实例,它传递任何参数给初始化程序来达到创建的目的。在对象生命周期结束时,调用del。让我们更近地观察下这3个神奇方法吧:

  8. __new__(cls,[…)

  9. 一个对象的实例化时__new__是第一个被调用的方法。在类中传递其他任何参数到__init__。new很少被使用,这样做确实有其目的,特别是当一个子类继承一个不可改变的类型(一个元组或一个字符串)时。我不打算再继续深入追求new的细节了,因为这不会产生多大用处,因为在PythonDocs内已经涵盖了一份巨详细的说明了。

  10. __init__(self,[…)

  11. 类的初始化。它会获得初始构建调用传过来的任何东西(举例来说就是,当我们调用x=SomeClass(10,‘foo’),__init__就会把传过来的10和‘foo’作为参数。init在Python的类定义中几乎普遍被使用)

  12. __del__(self)

  13. 如果__new__和init是对象的构造器,那么del就是析构器。它不实现声明为delx(这样的代码不会解释成x.del())的行为。相反,它定义为当一个对象被垃圾回收时的行为。这可能对可能需要额外清理的对象相当有用,比如sockets或文件对象。但要小心,如果对象仍处于存活状态而当被解释退出时,del没有保证就会被执行,因此这样的del不能作为良好的编码规范的替代。(就像当你完成操作总是要关闭一次连接。但事实上,del几乎永远不会执行,就因为它处于不安全情况被调用了。使用时保持警惕!)

  14. 把上述这些内容合在一起,就成了一份__init__和__del__的实际使用用例:

  15. fromos.pathimportjoin

  16. classFileObject:

  17. '''对文件对象的包装,确保文件在关闭时得到删除'''

def__init__(self,filepath='~',filename='sample.txt'):

#按filepath,读写模式打开名为filename的文件

self.file=open(join(filepath,filename),'r+')


def__del__(self):

self.file.close()

delself.file


3.使操作符在自定义类内工作

使用Python神奇方法的优势之一就是它提供了一种简单的方式能让对象的行为像内建类型。这意味着你可以避免用丑陋,反直觉和非标准方法执行基本运算。在某些语言中,通常会这样做:

ifinstance.equals(other_instance):

#dosomething


你也应该在Python确实会这样做,但同时它会增加用户的疑惑以及不必要的冗长。不同的库可能会对相同的运算采用不同的命名,这使得用户比平常干了更多的事。依靠神奇方法的力量,你可以定义一个方法(比如__eq__),然后带代替我们真实的意图:

ifinstance==other_instance:

#dosomething


现在你看到的是神奇方法力量的一部分。绝大多数都允许我们定义为运算符本身的意义,当用在我们自己定义的类上就像它们是内建类型。

3.1神奇方法——比较

Python有一整套神奇方法被设计用来通过操作符实现对象间直观的比较,而非别扭的方法调用。它们同样提供了一套覆盖Python对象比较的默认行为(通过引用)。以下是这些方法的列表以及做法:

__cmp__(self,other)

__cmp__是神奇方法中最基础的一个。实际上它实现所有比较操作符行为(<,==,!=,等),但它有可能不按你想要的方法工作(例如,一个实例是否等于另一个这取决于比较的准则,以及一个实例是否大于其他的这也取决于其他的准则)。如果selfother,则返回正整数。它通常是最好的定义,而不需要你一次就全定义好它们,但当你需要用类似的准则进行所有的比较时,cmp会是一个很好的方式,帮你节省重复性和提高明确度。

__eq__(self,other)


定义了相等操作符,==的行为。


__ne__(self,other)


定义了不相等操作符,!=的行为。


__lt__(self,other)


定义了小于操作符,<的行为。


__gt__(self,other)


定义了大于操作符,>的行为。


__le__(self,other)


定义了小于等于操作符,<=的行为。


__ge__(self,other)


定义了大于等于操作符,>=的行为。


举一个例子,设想对单词进行类定义。我们可能希望能够按内部对string的默认比较行为,即字典序(通过字母)来比较单词,也希望能够基于某些其他的准则,像是长度或音节数。在本例中,我们通过单词长度排序,以下给出实现:

classWord(str):

'''单词类,比较定义是基于单词长度的'''


def__new__(cls,word):

#注意,我们使用了__new__,这是因为str是一个不可变类型,

#所以我们必须更早地初始化它(在创建时)

if''inword:

print"单词内含有空格,截断到第一部分"

word=word[:word.index('')]#在出现第一个空格之前全是字符了现在

returnstr.__new__(cls,word)


def__gt__(self,other):

returnlen(self)>len(other)

def__lt__(self,other):

returnlen(self)

def__ge__(self,other):

returnlen(self)>=len(other)

def__le__(self,other):

returnlen(self)<=len(other)


现在,我们可以创建2个单词(通过Word(‘foo’)和Word(‘bar’))并基于它们的长度进行比较了。注意,我们没有定义__eq__和__ne__。这是因为这可能导致某些怪异的行为(特别是当比较Word(‘foo’)==Word(‘bar’)将会得到True的结果)。基于单词长度的相等比较会令人摸不清头脑,因此我们就沿用了str本身的相等比较的实现。

现在可能是一个好时机来提醒你一下,你不必重载每一个比较相关的神奇方法来获得各种比较。标准库已经友好地为我们在模板functools中提供了一个装饰(decorator)类,定义了所有比较方法。你可以只重载__eq__和一个其他的方法(比如__gt__,lt,等)。这个特性只在Python2.7(后?)适用,但当你有机会的话应该尝试一下,它会为你省下大量的时间和麻烦。你可以通过在你自己的重载方法在加上@total_ordering来使用。

3.2神奇方法——数字

就像你可以通过重载比较操作符的途径来创建你自己的类实例,你同样可以重载数字操作符。系好你们的安全带,朋友们,还有很多呢。处于本文组织的需要,我会把数字的神奇方法分割成5块:一元操作符,常规算术操作符,反射算术操作符,增量赋值,类型转换。

一元操作符

一元运算和函数仅有一个操作数,比如负数,绝对值等

__pos__(self)


实现一元正数的行为(如:+some_object)


__neg__(self)


实现负数的行为(如:-some_object)


__abs__(self)


实现内建abs()函数的行为


__invert__(self)


实现用~操作符进行的取反行为。你可以参考Wiki:bitwiseoperations来解释这个运算符究竟会干什么

常规算术操作符


现在我们涵盖了基本的二元运算符:+,-,*等等。其中大部分都是不言自明的。


__add__(self,other)


实现加法


__sub__(self,other)


实现减法


__mul__(self,other)


实现乘法


__floordiv__(self,other)


实现地板除法,使用//操作符


__div__(self,other)


实现传统除法,使用/操作符


__truediv__(self,other)


实现真正除法。注意,只有当你from__future__importdivision时才会有效


__mod__(self,other)


实现求模,使用%操作符


__divmod__(self,other)


实现内建函数divmod()的行为


__pow__(self,other)


实现乘方,使用**操作符


__lshift__(self,other)


实现左按位位移,使用<<操作符


__rshift__(self,other)


实现右按位位移,使用>>操作符


__and__(self,other)


实现按位与,使用&操作符


__or__(self,other)


实现按位或,使用|操作符


__xor__(self,other)


实现按位异或,使用^操作符


反射算术操作符

你知道我会如何解释反射算术操作符?你们中的有些人或许会觉得它很大,很可怕,是国外的概念。但它实际上很简单,下面给一个例子:

some_object+other

这是“常规的”加法。而反射其实相当于一回事,除了操作数改变了改变下位置:

other+some_object

因此,所有这些神奇的方法会做同样的事等价于常规算术操作符,除了改变操作数的位置关系,比如第一个操作数和自身作为第二个。此外没有其他的操作方式。在大多数情况下,反射算术操作的结果等价于常规算术操作,所以你尽可以在刚重载完__radd__就调用add。干脆痛快:

__radd__(self,other)


实现反射加法


__rsub__(self,other)


实现反射减法


__rmul__(self,other)


实现反射乘法


__rfloordiv__(self,other)


实现反射地板除,用//操作符


__rdiv__(self,other)


实现传统除法,用/操作符


__rturediv__(self,other)


实现真实除法,注意,只有当你from__future__importdivision时才会有效


__rmod__(self,other)


实现反射求模,用%操作符


__rdivmod__(self,other)


实现内置函数divmod()的长除行为,当调用divmod(other,self)时被调用


__rpow__(self,other)


实现反射乘方,用**操作符


__rlshift__(self,other)


实现反射的左按位位移,使用<<操作符


__rrshift__(self,other)


实现反射的右按位位移,使用>>操作符


__rand__(self,other)


实现反射的按位与,使用&操作符


__ror__(self,other)


实现反射的按位或,使用|操作符


__rxor__(self,other)


实现反射的按位异或,使用^操作符


增量赋值

Python也有各种各样的神奇方法允许用户自定义增量赋值行为。你可能已经熟悉增量赋值,它结合了“常规的”操作符和赋值。如果你仍不明白我在说什么,下面有一个例子:

x=5

x+=1#等价x=x+1


这些方法都不会有返回值,因为赋值在Python中不会有任何返回值。反而它们只是改变类的状态。列表如下:

__iadd__(self,other)


实现加法和赋值


__isub__(self,other)


实现减法和赋值


__imul__(self,other)


实现乘法和赋值


__ifloordiv__(self,other)


实现地板除和赋值,用//=操作符


__idiv__(self,other)


实现传统除法和赋值,用/=操作符


__iturediv__(self,other)


实现真实除法和赋值,注意,只有当你from__future__importdivision时才会有效


__imod__(self,other)


实现求模和赋值,用%=操作符


__ipow__(self,other)


实现乘方和赋值,用**=操作符


__ilshift__(self,other)


实现左按位位移和赋值,使用<<=操作符


__irshift__(self,other)


实现右按位位移和赋值,使用>>=操作符


__iand__(self,other)


实现按位与和赋值,使用&=操作符


__ior__(self,other)


实现按位或和赋值,使用|=操作符


__ixor__(self,other)


实现按位异或和赋值,使用^=操作符


类型转换的神奇方法

Python也有一组神奇方法被设计用来实现内置类型转换函数的行为,如float()

__int__(self)


实现到int的类型转换


__long__(self)


实现到long的类型转换


__float__(self)


实现到float的类型转换


__complex__(self)


实现到复数的类型转换


__oct__(self)


实现到8进制的类型转换


__hex__(self)


实现到16进制的类型转换


__index__(self)


实现一个当对象被切片到int的类型转换。如果你自定义了一个数值类型,考虑到它可能被切片,所以你应该重载__index__


__trunc__(self)


当math.trunc(self)被调用时调用。__trunc__应当返回一个整型的截断,(通常是long)


__coerce__(self,other)


该方法用来实现混合模式的算术。如果类型转换不可能那__coerce__应当返回None。否则,它应当返回一对包含self和other(2元组),且调整到具有相同的类型


4.描述你的类

用一个字符串来说明一个类这通常是有用的。在Python中提供了一些方法让你可以在你自己的类中自定义内建函数返回你的类行为的描述。

__str__(self)


当你定义的类中一个实例调用了str(),用于给它定义行为


__repr__(self)


当你定义的类中一个实例调用了repr(),用于给它定义行为。str()和repr()主要的区别在于它的阅读对象。repr()产生的输出主要为计算机可读(在很多情况下,这甚至可能是一些有效的Python代码),而str()则是为了让人类可读。


__unicode__(self)


当你定义的类中一个实例调用了unicode(),用于给它定义行为。unicode()像是str(),只不过它返回一个unicode字符串。警惕!如果用户用你的类中的一个实例调用了str(),而你仅定义了__unicode__(),那它是不会工作的。以防万一,你应当总是定义好__str__(),哪怕用户不会使用unicode


__hash__(self)


当你定义的类中一个实例调用了hash(),用于给它定义行为。它必须返回一个整型,而且它的结果是用于来在字典中作为快速键比对。


__nonzero__(self)


当你定义的类中一个实例调用了bool(),用于给它定义行为。返回True或False,取决于你是否考虑一个实例是True或False的。


我们已经相当漂亮地干完了神奇方法无聊的部分(无示例),至此我们已经讨论了一些基础的神奇方法,是时候让我们向高级话题移动了。

5.属性访问控制

有许多从其他语言阵营转到Python来的人抱怨Python对类缺乏真正的封装(比如,没有办法自定义private属性,已经给出public的getter和setter)。这可不是真相哟:Python通过神奇的方法实现了大量的封装,而不是通过明确的方法或字段修饰符。

请看:

__getattr__(self,name)

你可以为用户在试图访问不存在(不论是存在或尚未建立)的类属性时定义其行为。这对捕捉和重定向常见的拼写错误,给出使用属性警告是有用的(只要你愿意,你仍旧可选计算,返回那个属性)或抛出一个AttributeError异常。这个方法只适用于访问一个不存在的属性,所以,这不算一个真正封装的解决之道。

__setattr__(self,name,value)

不像__getattr__,setattr是一个封装的解决方案。它允许你为一个属性赋值时候的行为,不论这个属性是否存在。这意味着你可以给属性值的任意变化自定义规则。然而,你需要在意的是你要小心使用setattr,在稍后的列表中会作为例子给出。

__delattr__

这等价于__setattr__,但是作为删除类属性而不是set它们。它需要相同的预防措施,就像setattr,防止无限递归(当在delattr中调用delself.name会引起无限递归)。

__getattribute__(self,name)

__getattribute__良好地适合它的同伴们__setattr__和__delattr__。可我却不建议你使用它。getattribute只能在新式类中使用(在Python的最新版本中,所有的类都是新式类,在稍旧的版本中你可以通过继承object类来创建一个新式类。它允许你定规则,在任何时候不管一个类属性的值那时候是否可访问的。)它会因为他的同伴中的出错连坐受到某些无限递归问题的困扰(这时你可以通过调用基类的getattribute方法来防止发生)。当getattribute被实现而又只调用了该方法如果getattribute被显式调用或抛出一个AttributeError异常,同时也主要避免了对getattr的依赖。这个方法可以使用(毕竟,这是你自己的选择),不过我不推荐它是因为它有一个小小的用例(虽说比较少见,但我们需要特殊行为以获取一个值而不是赋值)以及它真的很难做到实现0bug。

你可以很容易地在你自定义任何类属性访问方法时引发一个问题。参考这个例子:

def__setattr__(self,name,value):

self.name=value

#当

ann
3楼 · 2021-02-25 14:54
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,keys=None, levels=None, names=None, verify_integrity=False,copy=True)

参数含义

 

  • objs:Series,DataFrame或Panel对象的序列或映射。如果传递了dict,则排序的键将用作键参数,除非它被传递,在这种情况下,将选择值(见下文)。任何无对象将被静默删除,除非它们都是无,在这种情况下将引发一个ValueError。

  • axis:{0,1,...},默认为0。沿着连接的轴。

  • join:{'inner','outer'},默认为“outer”。如何处理其他轴上的索引。outer为联合和inner为交集。

  • ignore_index:boolean,default False。如果为True,请不要使用并置轴上的索引值。结果轴将被标记为0,...,n-1。如果要连接其中并置轴没有有意义的索引信息的对象,这将非常有用。注意,其他轴上的索引值在连接中仍然受到尊重。

  • join_axes:Index对象列表。用于其他n-1轴的特定索引,而不是执行内部/外部设置逻辑。

  • keys:序列,默认值无。使用传递的键作为最外层构建层次索引。如果为多索引,应该使用元组。

  • levels:序列列表,默认值无。用于构建MultiIndex的特定级别(唯一值)。否则,它们将从键推断。

  • names:list,default无。结果层次索引中的级别的名称。

  • verify_integrity:boolean,default False。检查新连接的轴是否包含重复项。这相对于实际的数据串联可能是非常昂贵的。

  • copy:boolean,default True。如果为False,请勿不必要地复制数据。

In [1]: df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],...:                     'B': ['B0', 'B1', 'B2', 'B3'],...:                     'C': ['C0', 'C1', 'C2', 'C3'],...:                     'D': ['D0', 'D1', 'D2', 'D3']},...:                     index=[0, 1, 2, 3])...:In [2]: df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],...:                     'B': ['B4', 'B5', 'B6', 'B7'],...:                     'C': ['C4', 'C5', 'C6', 'C7'],...:                     'D': ['D4', 'D5', 'D6', 'D7']},...:                      index=[4, 5, 6, 7])...:In [3]: df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],...:                     'B': ['B8', 'B9', 'B10', 'B11'],...:                     'C': ['C8', 'C9', 'C10', 'C11'],...:                     'D': ['D8', 'D9', 'D10', 'D11']},...:                     index=[8, 9, 10, 11])...:In [4]: frames = [df1, df2, df3]In [5]: result = pd.concat(frames)


KEY参数

 

 

result = pd.concat(frames, keys=['x', 'y', 'z'])

 

JOIN参数

默认join = 'outer',为取并集的关系

 

In [8]: df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],...:                  'D': ['D2', 'D3', 'D6', 'D7'],...:                  'F': ['F2', 'F3', 'F6', 'F7']},...:                 index=[2, 3, 6, 7])...:In [9]: result = pd.concat([df1, df4], axis=1)

结果:

 


当设置join = 'inner',则说明为取交集

In [10]: result = pd.concat([df1, df4], axis=1, join='inner')

结果:

 

如果索引想从原始DataFrame重用确切索引

 

In [11]: result = pd.concat([df1, df4], axis=1, join_axes=[df1.index]) #设置索引为df1的索引


相关问题推荐

  • 回答 3

    换行。比如,print hello\nworld效果就是helloworld\n就是一个换行符。\是转义的意思,&#39;\n&#39;是换行,&#39;\t&#39;是tab,&#39;\\&#39;是,\ 是在编写程序中句子太长百,人为换行后加上\但print出来是一整行。...

  • 回答 42

    十种常见排序算法一般分为以下几种:(1)非线性时间比较类排序:a. 交换类排序(快速排序、冒泡排序)b. 插入类排序(简单插入排序、希尔排序)c. 选择类排序(简单选择排序、堆排序)d. 归并排序(二路归并排序、多路归并排序)(2)线性时间非比较类排序:...

  • 回答 70
    已采纳

    前景很好,中国正在产业升级,工业机器人和人工智能方面都会是强烈的热点,而且正好是在3~5年以后的时间。难度,肯定高,要求你有创新的思维能力,高数中的微积分、数列等等必须得非常好,软件编程(基础的应用最广泛的语言:C/C++)必须得很好,微电子(数字电...

  • 回答 28

    迭代器与生成器的区别:(1)生成器:生成器本质上就是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。而且记录了程序执行的上下文。生成器不仅记住了它的数据状态,生成器还记住了程序...

  • 回答 9

    python中title( )属于python中字符串函数,返回’标题化‘的字符串,就是单词的开头为大写,其余为小写

  • 回答 6

    第一种解释:代码中的cnt是count的简称,一种电脑计算机内部的数学函数的名字,在Excel办公软件中计算参数列表中的数字项的个数;在数据库( sq| server或者access )中可以用来统计符合条件的数据条数。函数COUNT在计数时,将把数值型的数字计算进去;但是...

  • 回答 1

    head是方法,所以需要取小括号,即dataset.head()显示的则是前5行。data[:, :-1]和data[:, -1]。另外,如果想通过位置取数据,请使用iloc,即dataset.iloc[:, :-1]和dataset.iloc[:, -1],前者表示的是取所有行,但不包括最后一列的数据,结果是个DataFrame。...

  • Python入门简单吗2021-09-23 13:21
    回答 45

    挺简单的,其实课程内容没有我们想象的那么难、像我之前同学,完全零基础,培训了半年,直接出来就工作了,人家还在北京大公司上班,一个月15k,实力老厉害了

  • 回答 4

    Python针对众多的类型,提供了众多的内建函数来处理(内建是相对于导入import来说的,后面学习到包package时,将会介绍),这些内建函数功用在于其往往可对多种类型对象进行类似的操作,即多种类型对象的共有的操作;如果某种操作只对特殊的某一类对象可行,Pyt...

  • 回答 8

     相当于 ... 这里不是注释

  • 回答 4

    还有FIXME

  • 回答 3

    python的两个库:xlrd和xlutils。 xlrd打开excel,但是打开的excel并不能直接写入数据,需要用xlutils主要是复制一份出来,实现后续的写入功能。

  • 回答 8

    单行注释:Python中的单行注释一般是以#开头的,#右边的文字都会被当做解释说明的内容,不会被当做执行的程序。为了保证代码的可读性,一般会在#后面加一两个空格然后在编写解释内容。示例:#  单行注释print(hello world)注释可以放在代码上面也可以放在代...

  • 回答 2

    主要是按行读取,然后就是写出判断逻辑来勘测行是否为注视行,空行,编码行其他的:import linecachefile=open(&#39;3_2.txt&#39;,&#39;r&#39;)linecount=len(file.readlines())linecache.getline(&#39;3_2.txt&#39;,linecount)这样做的过程中发现一个问题,...

  • 回答 4

    或许是里面有没被注释的代码

  • 回答 26

    自学的话要看个人情况,可以先在B站找一下视频看一下

没有解决我的问题,去提问