Python熊猫:您可能不知道的技巧和功能

帅比萌擦擦* 提交于 2020-10-05 05:41:44

熊猫是用于分析,数据处理和数据科学的基础库。这是一个庞大的项目,具有大量的可选项和深度。

如果您对Python的Pandas库的核心概念感到满意,希望您会在本文中找到以前从未偶然发现过的一两个窍门。

注意:本文中的示例已通过Pandas 0.23.2版和Python 3.6.6版进行了测试。但是,它们在旧版本中也应有效。配套课程请点击这里:

1.在解释器启动时配置选项和设置配套课程请点击这里:

您之前可能已经运行过Pandas的丰富选项和设置系统。

在解释器启动时设置自定义的Pandas选项可以节省大量的生产力,尤其是在脚本环境中工作时。您可以使用Python或IPython启动文件pd.set_option()来配置您的心脏内容。

这些选项使用点符号(例如)pd.set_option('display.max_colwidth', 25),很适合嵌套在选项的嵌套字典中:

import pandas as pd

def start():
    options = {
        'display': {
            'max_columns': None,
            'max_colwidth': 25,
            'expand_frame_repr': False,  # Don't wrap to multiple pages
            'max_rows': 14,
            'max_seq_items': 50,         # Max length of printed sequence
            'precision': 4,
            'show_dimensions': False
        },
        'mode': {
            'chained_assignment': None   # Controls SettingWithCopyWarning
        }
    }

    for category, option in options.items():
        for op, value in option.items():
            pd.set_option(f'{category}.{op}', value)  # Python 3.6+

if __name__ == '__main__':
    start()
    del start  # Clean up namespace in the interpreter

如果启动解释器会话,您将看到启动脚本中的所有内容均已执行,并且使用您的选项套件会自动为您导入Pandas:

>>> pd.__name__
'pandas'
>>> pd.get_option('display.max_rows')

14

让我们使用UCI机器学习存储库托管的鲍鱼上的一些数据来演示在启动文件中设置的格式。数据将在14行中截断,且浮点数的精度为4位:

>>> url = ('https://archive.ics.uci.edu/ml/'
...        'machine-learning-databases/abalone/abalone.data')
>>> cols = ['sex', 'length', 'diam', 'height', 'weight', 'rings']
>>> abalone = pd.read_csv(url, usecols=[0, 1, 2, 3, 4, 8], names=cols)

>>> abalone
     sex  length   diam  height  weight  rings
0      M   0.455  0.365   0.095  0.5140     15
1      M   0.350  0.265   0.090  0.2255      7
2      F   0.530  0.420   0.135  0.6770      9
3      M   0.440  0.365   0.125  0.5160     10
4      I   0.330  0.255   0.080  0.2050      7
5      I   0.425  0.300   0.095  0.3515      8
6      F   0.530  0.415   0.150  0.7775     20
# ...
4170   M   0.550  0.430   0.130  0.8395     10
4171   M   0.560  0.430   0.155  0.8675      8
4172   F   0.565  0.450   0.165  0.8870     11
4173   M   0.590  0.440   0.135  0.9660     10
4174   M   0.600  0.475   0.205  1.1760      9
4175   F   0.625  0.485   0.150  1.0945     10
4176   M   0.710  0.555   0.195  1.9485     12

稍后您还将在其他示例中看到此数据集弹出。

2.使用熊猫的测试模块制作玩具数据结构配套课程请点击这里:

注意:该pandas.util.testing模块在Pandas 1.0中已弃用。该“公开测试API”从pandas.testing现在仅限于assert_extension_array_equal(), assert_frame_equal(),assert_series_equal(),和assert_index_equal()。作者承认,他依靠熊猫图书馆未记载的部分而对自己的药物有所了解。

Pandas testing模块中隐藏的许多便捷功能可用于快速构建准现实的Series和DataFrames:

>>> import pandas.util.testing as tm
>>> tm.N, tm.K = 15, 3  # Module-level default rows/columns

>>> import numpy as np
>>> np.random.seed(444)

>>> tm.makeTimeDataFrame(freq='M').head()
                 A       B       C
2000-01-31  0.3574 -0.8804  0.2669
2000-02-29  0.3775  0.1526 -0.4803
2000-03-31  1.3823  0.2503  0.3008
2000-04-30  1.1755  0.0785 -0.1791
2000-05-31 -0.9393 -0.9039  1.1837

>>> tm.makeDataFrame().head()
                 A       B       C
nTLGGTiRHF -0.6228  0.6459  0.1251
WPBRn9jtsR -0.3187 -0.8091  1.1501
7B3wWfvuDA -1.9872 -1.0795  0.2987
yJ0BTjehH1  0.8802  0.7403 -1.2154
0luaYUYvy1 -0.9320  1.2912 -0.2907
其中大约有30个,您可以通过调用dir()模块对象来查看完整列表。这里有一些:

>>> [i for i in dir(tm) if i.startswith('make')]
['makeBoolIndex',
 'makeCategoricalIndex',
 'makeCustomDataframe',
 'makeCustomIndex',
 # ...,
 'makeTimeSeries',
 'makeTimedeltaIndex',
 'makeUIntIndex',
 'makeUnicodeIndex']

这些对于基准测试,测试断言以及尝试使用您不太熟悉的Pandas方法很有用。

3.利用访问器方法配套课程请点击这里:

也许您听说过accessor一词,它有点像一个getter(尽管getter和setter在Python中很少使用)。对于我们这里的目的,您可以将Pandas访问器视为一个属性,该属性充当其他方法的接口。

熊猫系列有三个:

>>> pd.Series._accessors
{'cat', 'str', 'dt'}

是的,上面的定义非常详尽,因此在讨论内部原理之前,让我们看一些示例。

.cat用于分类数据,.str用于字符串(对象)数据,以及.dt用于类似日期时间的数据。让我们开始.str:假设您在熊猫系列中有一些原始的城市/州/邮政编码数据作为单个字段。

Pandas字符串方法是矢量化的,这意味着它们可以在整个数组上运行而无需显式的for循环:

>>> addr = pd.Series([
...     'Washington, D.C. 20003',
...     'Brooklyn, NY 11211-1755',
...     'Omaha, NE 68154',
...     'Pittsburgh, PA 15211'
... ])

>>> addr.str.upper()
0     WASHINGTON, D.C. 20003
1    BROOKLYN, NY 11211-1755
2            OMAHA, NE 68154
3       PITTSBURGH, PA 15211
dtype: object

>>> addr.str.count(r'\d')  # 5 or 9-digit zip?
0    5
1    9
2    5
3    5
dtype: int64

对于更复杂的示例,假设您想将三个城市/州/邮政编码区域巧妙地分离到DataFrame字段中。

您可以传递正则表达式以.str.extract()“提取”系列中每个单元的部分。在中.str.extract(),.str是访问器,并且.str.extract()是访问器方法:

>>> regex = (r'(?P<city>[A-Za-z ]+), '      # One or more letters
...          r'(?P<state>[A-Z]{2}) '        # 2 capital letters
...          r'(?P<zip>\d{5}(?:-\d{4})?)')  # Optional 4-digit extension
...
>>> addr.str.replace('.', '').str.extract(regex)
         city state         zip
0  Washington    DC       20003
1    Brooklyn    NY  11211-1755
2       Omaha    NE       68154
3  Pittsburgh    PA       15211

这也说明了所谓的方法链接,.str.extract(regex)在的结果上调用addr.str.replace('.', ''),它清除了句点的使用以获得漂亮的2个字符的状态缩写。

稍微了解一下这些访问器方法的工作方式是很有帮助的,这是为什么您应该首先使用它们而不是类似的动机addr.apply(re.findall, ...)。

每个访问器本身都是真正的Python类:

.str映射到StringMethods。 .dt映射到CombinedDatetimelikeProperties。 .cat前往的路线CategoricalAccessor。 然后,使用将该“独立类”​​“附加”到Series类CachedAccessor。当类被包裹时CachedAccessor,发生了一些魔术。

CachedAccessor受到“缓存属性”设计的启发:每个实例仅计算一次属性,然后将其替换为普通属性。它通过重载.get()方法来实现此目的,该方法是Python描述符协议的一部分。

注:如果您想了解更多关于这个作品,是如何看待的内部Python的描述符HOWTO和这个职位上缓存的性能设计。还引入了Python 3 functools.lru_cache(),它提供了类似的功能。在该模式中到处都有示例,例如在aiohttp包装中。

第二个访问器,.dt用于类似日期时间的数据。从技术上讲它属于Pandas的DatetimeIndex,如果在Series上调用,它将转换为DatetimeIndex第一个:

>>> daterng = pd.Series(pd.date_range('2017', periods=9, freq='Q'))
>>> daterng
0   2017-03-31
1   2017-06-30
2   2017-09-30
3   2017-12-31
4   2018-03-31
5   2018-06-30
6   2018-09-30
7   2018-12-31
8   2019-03-31
dtype: datetime64[ns]

>>>  daterng.dt.day_name()
0      Friday
1      Friday
2    Saturday
3      Sunday
4    Saturday
5    Saturday
6      Sunday
7      Monday
8      Sunday
dtype: object

>>> # Second-half of year only
>>> daterng[daterng.dt.quarter > 2]
2   2017-09-30
3   2017-12-31
6   2018-09-30
7   2018-12-31
dtype: datetime64[ns]

>>> daterng[daterng.dt.is_year_end]
3   2017-12-31
7   2018-12-31
dtype: datetime64[ns]

第三个访问器,.cat仅用于分类数据,您将很快在其单独的部分中看到。

4.从组件列创建DatetimeIndex配套课程请点击这里:

daterng如上所述,谈到类似日期时间的数据,可以DatetimeIndex从多个组成日期或日期时间的组件列中创建一个Pandas :

>>> from itertools import product
>>> datecols = ['year', 'month', 'day']

>>> df = pd.DataFrame(list(product([2017, 2016], [1, 2], [1, 2, 3])),
...                   columns=datecols)
>>> df['data'] = np.random.randn(len(df))
>>> df
    year  month  day    data
0   2017      1    1 -0.0767
1   2017      1    2 -1.2798
2   2017      1    3  0.4032
3   2017      2    1  1.2377
4   2017      2    2 -0.2060
5   2017      2    3  0.6187
6   2016      1    1  2.3786
7   2016      1    2 -0.4730
8   2016      1    3 -2.1505
9   2016      2    1 -0.6340
10  2016      2    2  0.7964
11  2016      2    3  0.0005

>>> df.index = pd.to_datetime(df[datecols])
>>> df.head()
            year  month  day    data
2017-01-01  2017      1    1 -0.0767
2017-01-02  2017      1    2 -1.2798
2017-01-03  2017      1    3  0.4032
2017-02-01  2017      2    1  1.2377
2017-02-02  2017      2    2 -0.2060
最后,您可以删除旧的单个列并转换为Series:

>>> df = df.drop(datecols, axis=1).squeeze()
>>> df.head()
2017-01-01   -0.0767
2017-01-02   -1.2798
2017-01-03    0.4032
2017-02-01    1.2377
2017-02-02   -0.2060
Name: data, dtype: float64

>>> df.index.dtype_str
'datetime64[ns]

传递DataFrame的直觉是,DataFrame类似于Python字典,其中列名是键,而各个列(系列)是字典值。这就是为什么pd.to_datetime(df[datecols].to_dict(orient='list'))在这种情况下也可以工作的原因。这反映了Python的构造datetime.datetime,您可以在其中传递诸如的关键字参数datetime.datetime(year=2000, month=1, day=15, hour=10)。

5.使用分类数据节省时间和空间配套课程请点击这里:

Categoricaldtype 是Pandas的一项强大功能。

即使您不总是在RAM中处理千兆字节的数据,您也可能会遇到在大型DataFrame上进行简单操作挂起超过几秒钟的情况。

熊猫objectdtype通常是转换为类别数据的理想选择。(object是Python str,异构数据类型或“其他”类型的容器。)字符串在内存中占据了大量空间:

>>> colors = pd.Series([
...     'periwinkle',
...     'mint green',
...     'burnt orange',
...     'periwinkle',
...     'burnt orange',
...     'rose',
...     'rose',
...     'mint green',
...     'rose',
...     'navy'
... ])
...
>>> import sys
>>> colors.apply(sys.getsizeof)
0    59
1    59
2    61
3    59
4    61
5    53
6    53
7    59
8    53
9    53
dtype: int64

注意:我过去常常sys.getsizeof()显示Series中每个单独值所占用的内存。请记住,这些是Python对象,首先要有一些开销。(sys.getsizeof('')将返回49个字节。)

还有colors.memory_usage(),它总结了内存使用情况,并依赖于.nbytes基础NumPy数组的属性。不要对这些细节感到困惑:重要的是类型转换产生的相对内存使用率,正如您将在接下来看到的。

现在,如果我们可以采用上面的独特颜色并将每个颜色映射到一个不占用空间的整数,该怎么办?这是一个简单的实现:

>>> mapper = {v: k for k, v in enumerate(colors.unique())}
>>> mapper
{'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}

>>> as_int = colors.map(mapper)
>>> as_int
0    0
1    1
2    2
3    0
4    2
5    3
6    3
7    1
8    3
9    4
dtype: int64

>>> as_int.apply(sys.getsizeof)
0    24
1    28
2    28
3    24
4    28
5    28
6    28
7    28
8    28
9    28
dtype: int64

注意:另一种执行此操作的方法是使用Pandas' pd.factorize(colors):

>>> pd.factorize(colors)[0]
array([0, 1, 2, 0, 2, 3, 3, 1, 3, 4])

无论哪种方式,您都将对象编码为枚举类型(分类变量)。

您会立即注意到,与将完整字符串与objectdtype 一起使用时相比,内存使用量大约减少了一半。

在上一节早些时候存取,我所提到的.cat(分类)访问。上面mapper的内容粗略地说明了熊猫的Categoricaldtype 内部发生的情况:

“ a的内存使用量与Categorical类别数量和数据长度成正比。相比之下,objectdtype是数据长度的常数倍。” (资源)

在colors上面,每个唯一值(类别)的比率为2:

>>> len(colors) / colors.nunique()
2.0

结果,从转换为Categorical可以节省内存,但是效果不佳:

>>> # Not a huge space-saver to encode as Categorical
>>> colors.memory_usage(index=False, deep=True)
650
>>> colors.astype('category').memory_usage(index=False, deep=True)
495

但是,如果您超出上述比例,却拥有大量数据和很少的唯一值(考虑人口统计数据或字母测试分数的数据),则所需内存减少了10倍以上:

>>> manycolors = colors.repeat(10)
>>> len(manycolors) / manycolors.nunique()  # Much greater than 2.0x
20.0

>>> manycolors.memory_usage(index=False, deep=True)
6500
>>> manycolors.astype('category').memory_usage(index=False, deep=True)
585

一个额外的好处是计算效率也得到提高:对于分类Series,字符串操作是在.cat.categories属性上而不是在的每个原始元素上执行的Series。

换句话说,该操作对每个唯一类别执行一次,然后将结果映射回这些值。分类数据具有一个.cat访问器,该访问器是用于操纵类别的属性和方法的窗口:

>>> ccolors = colors.astype('category')
>>> ccolors.cat.categories
Index(['burnt orange', 'mint green', 'navy', 'periwinkle', 'rose'], dtype='object')

实际上,您可以重现与上面示例类似的内容:

>>> ccolors.cat.codes
0    3
1    1
2    0
3    3
4    0
5    4
6    4
7    1
8    4
9    2
dtype: int8

要完全模仿早期的手动输出,您需要做的就是对代码重新排序:

>>> ccolors.cat.reorder_categories(mapper).cat.codes
0    0
1    1
2    2
3    0
4    2
5    3
6    3
7    1
8    3
9    4
dtype: int8

请注意,dtype是NumPy's int8,这是一个8位带符号整数,可以接受-127到128之间的值。(只需要一个字节就可以表示内存中的值。ints就内存使用而言,64位带符号将是过大的杀伤力。)我们的粗略示例int64默认情况下会生成数据,而Pandas足够聪明,可以将分类数据转换为可能的最小数字dtype。

的大多数属性.cat与查看和操作基础类别本身有关:

>>> [i for i in dir(ccolors.cat) if not i.startswith('_')]
['add_categories',
 'as_ordered',
 'as_unordered',
 'categories',
 'codes',
 'ordered',
 'remove_categories',
 'remove_unused_categories',
 'rename_categories',
 'reorder_categories',
 'set_categories']

不过,有一些警告。分类数据通常不太灵活。例如,如果插入以前看不见的值,则需要首先将此值添加到.categories容器中:

>>> ccolors.iloc[5] = 'a new color'
# ...
ValueError: Cannot setitem on a Categorical with a new category,
set the categories first

>>> ccolors = ccolors.cat.add_categories(['a new color'])
>>> ccolors.iloc[5] = 'a new color'  # No more ValueError

如果您打算设置值或重塑数据而不是派生新的计算,则Categorical类型可能不太灵活。

6.通过迭代自检Groupby对象配套课程请点击这里:

调用时df.groupby('x'),生成的Pandas groupby对象可能有点不透明。该对象是延迟实例化的,并且本身没有任何有意义的表示。

您可以使用示例1中的鲍鱼数据集进行演示:

>>> abalone['ring_quartile'] = pd.qcut(abalone.rings, q=4, labels=range(1, 5))
>>> grouped = abalone.groupby('ring_quartile')

>>> grouped
<pandas.core.groupby.groupby.DataFrameGroupBy object at 0x11c1169b0>

好了,现在您有了一个groupby对象,但是这是什么东西,我怎么看?

在调用之类的东西之前grouped.apply(func),您可以利用groupby对象可迭代的事实:

>>> help(grouped.__iter__)

        Groupby iterator

        Returns
        -------
        Generator yielding sequence of (name, subsetted object)
        for each group

由产生的每个“事物” grouped.iter()都是的元组(name, subsetted object),其中name是您要分组的列的值,并且subsetted object是一个DataFrame,它是根据您指定的任何分组条件作为原始DataFrame的子集。也就是说,数据按组进行分块:

>>> for idx, frame in grouped:
...     print(f'Ring quartile: {idx}')
...     print('-' * 16)
...     print(frame.nlargest(3, 'weight'), end='\n\n')
...
Ring quartile: 1
----------------
     sex  length   diam  height  weight  rings ring_quartile
2619   M   0.690  0.540   0.185  1.7100      8             1
1044   M   0.690  0.525   0.175  1.7005      8             1
1026   M   0.645  0.520   0.175  1.5610      8             1

Ring quartile: 2
----------------
     sex  length  diam  height  weight  rings ring_quartile
2811   M   0.725  0.57   0.190  2.3305      9             2
1426   F   0.745  0.57   0.215  2.2500      9             2
1821   F   0.720  0.55   0.195  2.0730      9             2

Ring quartile: 3
----------------
     sex  length  diam  height  weight  rings ring_quartile
1209   F   0.780  0.63   0.215   2.657     11             3
1051   F   0.735  0.60   0.220   2.555     11             3
3715   M   0.780  0.60   0.210   2.548     11             3

Ring quartile: 4
----------------
     sex  length   diam  height  weight  rings ring_quartile
891    M   0.730  0.595    0.23  2.8255     17             4
1763   M   0.775  0.630    0.25  2.7795     12             4
165    M   0.725  0.570    0.19  2.5500     14             4

相关地,一个groupby对象也具有.groups和一个组获取器.get_group():

>>> grouped.groups.keys()
dict_keys([1, 2, 3, 4])

>>> grouped.get_group(2).head()
   sex  length   diam  height  weight  rings ring_quartile
2    F   0.530  0.420   0.135  0.6770      9             2
8    M   0.475  0.370   0.125  0.5095      9             2
19   M   0.450  0.320   0.100  0.3810      9             2
23   F   0.550  0.415   0.135  0.7635      9             2
39   M   0.355  0.290   0.090  0.3275      9             2
这可以帮助您更加确信自己正在执行的操作就是您想要的操作:

>>> grouped['height', 'weight'].agg(['mean', 'median'])
               height         weight
                 mean median    mean  median
ring_quartile
1              0.1066  0.105  0.4324  0.3685
2              0.1427  0.145  0.8520  0.8440
3              0.1572  0.155  1.0669  1.0645
4              0.1648  0.165  1.1149  1.0655

无论您执行哪种计算grouped,无论是单个Pandas方法还是自定义函数,这些“子帧”中的每一个都会作为参数传递给该可调用对象。这就是“拆分应用合并”一词的来源:按组分解数据,按组进行计算,然后以某种聚合方式重新组合。

如果您无法准确地看到组的实际外观,则只需遍历它们并打印一些即可非常有用。

7.使用此映射技巧进行会员分组配套课程请点击这里:

假设您有一个Series和一个对应的“映射表”,其中每个值都属于一个多成员组,或者根本不属于任何组:

>>> countries = pd.Series([
...     'United States',
...     'Canada',
...     'Mexico',
...     'Belgium',
...     'United Kingdom',
...     'Thailand'
... ])
...
>>> groups = {
...     'North America': ('United States', 'Canada', 'Mexico', 'Greenland'),
...     'Europe': ('France', 'Germany', 'United Kingdom', 'Belgium')
... }

换句话说,您需要映射countries到以下结果:

0    North America
1    North America
2    North America
3           Europe
4           Europe
5            other
dtype: object

这里您需要的功能类似于Pandas的功能pd.cut(),但用于基于分类成员资格的装箱。您可以使用pd.Series.map()在示例5中已经看到的来模仿这一点:

from typing import Any

def membership_map(s: pd.Series, groups: dict,
                   fillvalue: Any=-1) -> pd.Series:
    # Reverse & expand the dictionary key-value pairs
    groups = {x: k for k, v in groups.items() for x in v}
    return s.map(groups).fillna(fillvalue)

这应该比遍历groups每个国家的嵌套Python循环快得多countries。

这是一个试驾:

>>> membership_map(countries, groups, fillvalue='other')
0    North America
1    North America
2    North America
3           Europe
4           Europe
5            other
dtype: object

让我们分解一下这里发生的事情。(旁注:这是使用Python的调试器进入函数范围的好地方pdb,以检查该函数局部的变量。)

目的是将每个组映射groups为一个整数。但是,Series.map()将无法识别'ab'-它需要将每个组中的每个字符映射为一个整数的细分版本。这是字典理解的作用:

>>> groups = dict(enumerate(('ab', 'cd', 'xyz')))
>>> {x: k for k, v in groups.items() for x in v}
{'a': 0, 'b': 0, 'c': 1, 'd': 1, 'x': 2, 'y': 2, 'z': 2}

可以传递此字典以s.map()将其值映射或“转换”为其相应的组索引。

8.了解熊猫如何使用布尔运算符配套课程请点击这里:

您可能熟悉Python的运算符优先级,其中and,not和or比算术运算符,如较低的优先级<,<=,>,>=,!=,和==。考虑下面,这里的两个语句<,并>具有比更高的优先级and操作:

>>> # Evaluates to "False and True"
>>> 4 < 3 and 5 > 4
False

>>> # Evaluates to 4 < 5 > 4
>>> 4 < (3 and 5) > 4
True

注意:它与Pandas无关,但由于短路评估而3 and 5评估为5:

“短路算子的返回值是最后计算的参数。”

熊猫(和NumPy的,其上熊猫内置)不使用and,or或not。相反,它分别使用&,|和和~,它们是普通的,善意的Python按位运算符。

这些运算符不是熊猫“发明”的。相反,&,|,和~有效的Python内置具有较高(而不是更低)的优先级比算术运算符运营商。(Pandas覆盖了诸如此类的.ror()映射到|运算符的dunder方法。)要牺牲一些细节,您可以将“按位”视为“按元素”,因为它与Pandas和NumPy有关:

>>> pd.Series([True, True, False]) & pd.Series([True, False, False])
0     True
1    False
2    False
dtype: bool

充分理解这个概念是值得的。假设您有一个类似范围的系列:

>>> s = pd.Series(range(10))

我猜您可能在某个时候看到了此异常:

>>> s % 2 == 0 & s > 3
ValueError: The truth value of a Series is ambiguous.
Use a.empty, a.bool(), a.item(), a.any() or a.all().

这里发生了什么事?用括号将表达式增量绑定会很有帮助,阐明Python如何逐步扩展该表达式:

s % 2 == 0 & s > 3                      # Same as above, original expression
(s % 2) == 0 & s > 3                    # Modulo is most tightly binding here
(s % 2) == (0 & s) > 3                  # Bitwise-and is second-most-binding
(s % 2) == (0 & s) and (0 & s) > 3      # Expand the statement
((s % 2) == (0 & s)) and ((0 & s) > 3)  # The `and` operator is least-binding
该表达式s % 2 == 0 & s > 3等效于(或被视为)((s % 2) == (0 & s)) and ((0 & s) > 3)。这称为扩展:x < y <= z等效于x < y and y <= z。

好吧,现在停在那儿,让我们把它带回给熊猫讲。你有两个熊猫系列,我们会打电话left和right:

>>> left = (s % 2) == (0 & s)
>>> right = (0 & s) > 3
>>> left and right  # This will raise the same ValueError
您知道形式的声明left and right同时测试了left和的真值right,如下所示:

>>> bool(left) and bool(right)

问题在于,熊猫开发人员有意不为整个系列建立真值(真实性)。系列是对还是错?谁知道?结果是模棱两可的:

>>> bool(s)
ValueError: The truth value of a Series is ambiguous.
Use a.empty, a.bool(), a.item(), a.any() or a.all().
唯一有意义的比较是元素比较。这就是为什么,如果涉及算术运算符,则需要括号:

>>> (s % 2 == 0) & (s > 3)
0    False
1    False
2    False
3    False
4     True
5    False
6     True
7    False
8     True
9    False
dtype: bool

简而言之,如果您看到ValueError上面带有布尔索引的弹出窗口,那么您可能应该做的第一件事就是撒上一些需要的括号。

9.从剪贴板加载数据配套课程请点击这里:

通常需要将数据从Excel或Sublime Text等位置传输到Pandas数据结构。理想情况下,您希望这样做而无需执行将数据保存到文件然后再将文件读入Pandas的中间步骤。

您可以使用来从计算机剪贴板数据缓冲区中加载DataFrames pd.read_clipboard()。其关键字参数传递给pd.read_table()。

这使您可以将结构化文本直接复制到DataFrame或Series。在Excel中,数据如下所示:

Excel剪贴板数据

它的纯文本表示形式(例如,在文本编辑器中)如下所示:

a   b           c       d
0   1           inf     1/1/00
2   7.389056099 N/A     5-Jan-13
4   54.59815003 nan     7/24/18
6   403.4287935 None    NaT
只需突出显示并复制上面的纯文本,然后致电pd.read_clipboard():

>>> df = pd.read_clipboard(na_values=[None], parse_dates=['d'])
>>> df
   a         b    c          d
0  0    1.0000  inf 2000-01-01
1  2    7.3891  NaN 2013-01-05
2  4   54.5982  NaN 2018-07-24
3  6  403.4288  NaN        NaT

>>> df.dtypes
a             int64
b           float64
c           float64
d    datetime64[ns]
dtype: object

10.直接将熊猫对象写入压缩格式配套课程请点击这里:

这是一个简短而甜蜜的清单。从Pandas 0.21.0版开始,您可以将Pandas对象直接写入gzip,bz2,zip或xz压缩,而不必将未压缩的文件存储在内存中并进行转换。这是一个使用技巧#1的abalone数据的示例:

abalone.to_json('df.json.gz', orient='records',
                lines=True, compression='gzip')

在这种情况下,大小差为11.6倍:

>>> import os.path
>>> abalone.to_json('df.json', orient='records', lines=True)
>>> os.path.getsize('df.json') / os.path.getsize('df.json.gz')
11.603035760226396

希望您能够从此列表中选出一些有用的技巧,以使您的Pandas代码具有更好的可读性,多功能性和性能。

更多文章和资料|点击下方文字直达 ↓↓↓ 阿里云K8s实战手册 [阿里云CDN排坑指南]CDN ECS运维指南 DevOps实践手册 Hadoop大数据实战手册 Knative云原生应用开发指南 OSS 运维实战手册

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!