40 votes

Pourquoi les fonctions numpy sont-elles si lentes sur les séries / images de pandas?

Considérons une petite MWE, prises à partir d' une autre question:

DateTime                Data
2017-11-21 18:54:31     1
2017-11-22 02:26:48     2
2017-11-22 10:19:44     3
2017-11-22 15:11:28     6
2017-11-22 23:21:58     7
2017-11-28 14:28:28    28
2017-11-28 14:36:40     0
2017-11-28 14:59:48     1

L'objectif est de clip toutes les valeurs de la limite supérieure de 1. Ma réponse utilise np.clip, ce qui fonctionne très bien.

np.clip(df.Data, a_min=None, a_max=1)
array([1, 1, 1, 1, 1, 1, 0, 1])

Ou,

np.clip(df.Data.values, a_min=None, a_max=1)
array([1, 1, 1, 1, 1, 1, 0, 1])

Qui retournent la même réponse. Ma question est au sujet de la performance relative de ces deux méthodes. Considérez -

df = pd.concat([df]*1000).reset_index(drop=True)

%timeit np.clip(df.Data, a_min=None, a_max=1)
1000 loops, best of 3: 270 µs per loop

%timeit np.clip(df.Data.values, a_min=None, a_max=1)
10000 loops, best of 3: 23.4 µs per loop

Pourquoi il y a une énorme différence entre les deux, tout en appelant values sur le dernier? En d'autres mots...

Pourquoi numpy fonctions si lent sur les pandas objets?

50voto

MSeifert Points 6307

Oui, il semble que l' np.clip est beaucoup plus lent sur pandas.Series que sur numpy.ndarrays. C'est exact, mais c'est en fait (au moins asymptomatiques) n'est pas si mal. 8000 éléments est encore dans le régime où les facteurs constants sont les principaux contributeurs à l'exécution. Je pense que c'est un aspect très important de la question, donc je suis à la visualisation de cette (emprunts d' une autre réponse):

# Setup

import pandas as pd
import numpy as np

def on_series(s):
    return np.clip(s, a_min=None, a_max=1)

def on_values_of_series(s):
    return np.clip(s.values, a_min=None, a_max=1)

# Timing setup
timings = {on_series: [], on_values_of_series: []}
sizes = [2**i for i in range(1, 26, 2)]

# Timing
for size in sizes:
    func_input = pd.Series(np.random.randint(0, 30, size=size))
    for func in timings:
        res = %timeit -o func(func_input)
        timings[func].append(res)

%matplotlib notebook

import matplotlib.pyplot as plt
import numpy as np

fig, (ax1, ax2) = plt.subplots(1, 2)

for func in timings:
    ax1.plot(sizes, 
             [time.best for time in timings[func]], 
             label=str(func.__name__))
ax1.set_xscale('log')
ax1.set_yscale('log')
ax1.set_xlabel('size')
ax1.set_ylabel('time [seconds]')
ax1.grid(which='both')
ax1.legend()

baseline = on_values_of_series # choose one function as baseline
for func in timings:
    ax2.plot(sizes, 
             [time.best / ref.best for time, ref in zip(timings[func], timings[baseline])], 
             label=str(func.__name__))
ax2.set_yscale('log')
ax2.set_xscale('log')
ax2.set_xlabel('size')
ax2.set_ylabel('time relative to {}'.format(baseline.__name__))
ax2.grid(which='both')
ax2.legend()

plt.tight_layout()

enter image description here

C'est un graphe log-log, car je pense que cela illustre l'importance des caractéristiques plus clairement. Par exemple, il montre que l' np.clip sur numpy.ndarray est plus rapide, mais il est également beaucoup plus petit facteur constant dans ce cas. La différence pour les grands tableaux est seulement ~3! C'est quand même une grande différence, mais beaucoup moins que la différence sur de petits tableaux.

Cependant, ce n'est toujours pas une réponse à la question " d'où la différence de temps vient.

La solution est en fait assez simple: np.clip des délégués à l' clip méthode du premier argument:

>>> np.clip??
Source:   
def clip(a, a_min, a_max, out=None):
    """
    ...
    """
    return _wrapfunc(a, 'clip', a_min, a_max, out=out)

>>> np.core.fromnumeric._wrapfunc??
Source:   
def _wrapfunc(obj, method, *args, **kwds):
    try:
        return getattr(obj, method)(*args, **kwds)
    # ...
    except (AttributeError, TypeError):
        return _wrapit(obj, method, *args, **kwds)

L' getattr ligne de l' _wrapfunc de la fonction est le plus important ici, car np.ndarray.clip et pd.Series.clip sont des méthodes différentes, oui, des méthodes complètement différentes:

>>> np.ndarray.clip
<method 'clip' of 'numpy.ndarray' objects>
>>> pd.Series.clip
<function pandas.core.generic.NDFrame.clip>

Malheureusement, est - np.ndarray.clip C-fonction de sorte qu'il est difficile de profil, cependant, pd.Series.clip est régulièrement une fonction Python il est donc facile de profil. Nous allons utiliser une Série de 5000 entiers ici:

s = pd.Series(np.random.randint(0, 100, 5000))

Pour l' np.clip sur le values - je obtenir la ligne de profilage:

%load_ext line_profiler
%lprun -f np.clip -f np.core.fromnumeric._wrapfunc np.clip(s.values, a_min=None, a_max=1)

Timer unit: 4.10256e-07 s

Total time: 2.25641e-05 s
File: numpy\core\fromnumeric.py
Function: clip at line 1673

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  1673                                           def clip(a, a_min, a_max, out=None):
  1674                                               """
  ...
  1726                                               """
  1727         1           55     55.0    100.0      return _wrapfunc(a, 'clip', a_min, a_max, out=out)

Total time: 1.51795e-05 s
File: numpy\core\fromnumeric.py
Function: _wrapfunc at line 55

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    55                                           def _wrapfunc(obj, method, *args, **kwds):
    56         1            2      2.0      5.4      try:
    57         1           35     35.0     94.6          return getattr(obj, method)(*args, **kwds)
    58                                           
    59                                               # An AttributeError occurs if the object does not have
    60                                               # such a method in its class.
    61                                           
    62                                               # A TypeError occurs if the object does have such a method
    63                                               # in its class, but its signature is not identical to that
    64                                               # of NumPy's. This situation has occurred in the case of
    65                                               # a downstream library like 'pandas'.
    66                                               except (AttributeError, TypeError):
    67                                                   return _wrapit(obj, method, *args, **kwds)

Mais pour l' np.clip sur le Series - je obtenir un totalement différent de profilage résultat:

%lprun -f np.clip -f np.core.fromnumeric._wrapfunc -f pd.Series.clip -f pd.Series._clip_with_scalar np.clip(s, a_min=None, a_max=1)

Timer unit: 4.10256e-07 s

Total time: 0.000823794 s
File: numpy\core\fromnumeric.py
Function: clip at line 1673

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  1673                                           def clip(a, a_min, a_max, out=None):
  1674                                               """
  ...
  1726                                               """
  1727         1         2008   2008.0    100.0      return _wrapfunc(a, 'clip', a_min, a_max, out=out)

Total time: 0.00081846 s
File: numpy\core\fromnumeric.py
Function: _wrapfunc at line 55

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    55                                           def _wrapfunc(obj, method, *args, **kwds):
    56         1            2      2.0      0.1      try:
    57         1         1993   1993.0     99.9          return getattr(obj, method)(*args, **kwds)
    58                                           
    59                                               # An AttributeError occurs if the object does not have
    60                                               # such a method in its class.
    61                                           
    62                                               # A TypeError occurs if the object does have such a method
    63                                               # in its class, but its signature is not identical to that
    64                                               # of NumPy's. This situation has occurred in the case of
    65                                               # a downstream library like 'pandas'.
    66                                               except (AttributeError, TypeError):
    67                                                   return _wrapit(obj, method, *args, **kwds)

Total time: 0.000804922 s
File: pandas\core\generic.py
Function: clip at line 4969

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  4969                                               def clip(self, lower=None, upper=None, axis=None, inplace=False,
  4970                                                        *args, **kwargs):
  4971                                                   """
  ...
  5021                                                   """
  5022         1           12     12.0      0.6          if isinstance(self, ABCPanel):
  5023                                                       raise NotImplementedError("clip is not supported yet for panels")
  5024                                           
  5025         1           10     10.0      0.5          inplace = validate_bool_kwarg(inplace, 'inplace')
  5026                                           
  5027         1           69     69.0      3.5          axis = nv.validate_clip_with_axis(axis, args, kwargs)
  5028                                           
  5029                                                   # GH 17276
  5030                                                   # numpy doesn't like NaN as a clip value
  5031                                                   # so ignore
  5032         1          158    158.0      8.1          if np.any(pd.isnull(lower)):
  5033         1            3      3.0      0.2              lower = None
  5034         1           26     26.0      1.3          if np.any(pd.isnull(upper)):
  5035                                                       upper = None
  5036                                           
  5037                                                   # GH 2747 (arguments were reversed)
  5038         1            1      1.0      0.1          if lower is not None and upper is not None:
  5039                                                       if is_scalar(lower) and is_scalar(upper):
  5040                                                           lower, upper = min(lower, upper), max(lower, upper)
  5041                                           
  5042                                                   # fast-path for scalars
  5043         1            1      1.0      0.1          if ((lower is None or (is_scalar(lower) and is_number(lower))) and
  5044         1           28     28.0      1.4                  (upper is None or (is_scalar(upper) and is_number(upper)))):
  5045         1         1654   1654.0     84.3              return self._clip_with_scalar(lower, upper, inplace=inplace)
  5046                                           
  5047                                                   result = self
  5048                                                   if lower is not None:
  5049                                                       result = result.clip_lower(lower, axis, inplace=inplace)
  5050                                                   if upper is not None:
  5051                                                       if inplace:
  5052                                                           result = self
  5053                                                       result = result.clip_upper(upper, axis, inplace=inplace)
  5054                                           
  5055                                                   return result

Total time: 0.000662153 s
File: pandas\core\generic.py
Function: _clip_with_scalar at line 4920

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  4920                                               def _clip_with_scalar(self, lower, upper, inplace=False):
  4921         1            2      2.0      0.1          if ((lower is not None and np.any(isna(lower))) or
  4922         1           25     25.0      1.5                  (upper is not None and np.any(isna(upper)))):
  4923                                                       raise ValueError("Cannot use an NA value as a clip threshold")
  4924                                           
  4925         1           22     22.0      1.4          result = self.values
  4926         1          571    571.0     35.4          mask = isna(result)
  4927                                           
  4928         1           95     95.0      5.9          with np.errstate(all='ignore'):
  4929         1            1      1.0      0.1              if upper is not None:
  4930         1          141    141.0      8.7                  result = np.where(result >= upper, upper, result)
  4931         1           33     33.0      2.0              if lower is not None:
  4932                                                           result = np.where(result <= lower, lower, result)
  4933         1           73     73.0      4.5          if np.any(mask):
  4934                                                       result[mask] = np.nan
  4935                                           
  4936         1           90     90.0      5.6          axes_dict = self._construct_axes_dict()
  4937         1          558    558.0     34.6          result = self._constructor(result, **axes_dict).__finalize__(self)
  4938                                           
  4939         1            2      2.0      0.1          if inplace:
  4940                                                       self._update_inplace(result)
  4941                                                   else:
  4942         1            1      1.0      0.1              return result

J'ai cessé d'aller dans le sous-routines à ce point, car il souligne déjà où l' pd.Series.clip ne prend beaucoup plus de travail que l' np.ndarray.clip. Il suffit de comparer le temps total de l' np.clip appel sur l' values (55 minuterie unités) à l'un des premiers contrôles dans l' pandas.Series.clip méthode, l' if np.any(pd.isnull(lower)) (158 minuterie unités). À ce stade, les pandas méthode n'a même pas commencer à l'écrêtage et qu'il faut déjà 3 fois plus longtemps.

Cependant, plusieurs de ces "frais généraux" de plus en plus négligeables lorsque le tableau est grand:

s = pd.Series(np.random.randint(0, 100, 1000000))

%lprun -f np.clip -f np.core.fromnumeric._wrapfunc -f pd.Series.clip -f pd.Series._clip_with_scalar np.clip(s, a_min=None, a_max=1)

Timer unit: 4.10256e-07 s

Total time: 0.00593476 s
File: numpy\core\fromnumeric.py
Function: clip at line 1673

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  1673                                           def clip(a, a_min, a_max, out=None):
  1674                                               """
  ...
  1726                                               """
  1727         1        14466  14466.0    100.0      return _wrapfunc(a, 'clip', a_min, a_max, out=out)

Total time: 0.00592779 s
File: numpy\core\fromnumeric.py
Function: _wrapfunc at line 55

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    55                                           def _wrapfunc(obj, method, *args, **kwds):
    56         1            1      1.0      0.0      try:
    57         1        14448  14448.0    100.0          return getattr(obj, method)(*args, **kwds)
    58                                           
    59                                               # An AttributeError occurs if the object does not have
    60                                               # such a method in its class.
    61                                           
    62                                               # A TypeError occurs if the object does have such a method
    63                                               # in its class, but its signature is not identical to that
    64                                               # of NumPy's. This situation has occurred in the case of
    65                                               # a downstream library like 'pandas'.
    66                                               except (AttributeError, TypeError):
    67                                                   return _wrapit(obj, method, *args, **kwds)

Total time: 0.00591302 s
File: pandas\core\generic.py
Function: clip at line 4969

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  4969                                               def clip(self, lower=None, upper=None, axis=None, inplace=False,
  4970                                                        *args, **kwargs):
  4971                                                   """
  ...
  5021                                                   """
  5022         1           17     17.0      0.1          if isinstance(self, ABCPanel):
  5023                                                       raise NotImplementedError("clip is not supported yet for panels")
  5024                                           
  5025         1           14     14.0      0.1          inplace = validate_bool_kwarg(inplace, 'inplace')
  5026                                           
  5027         1           97     97.0      0.7          axis = nv.validate_clip_with_axis(axis, args, kwargs)
  5028                                           
  5029                                                   # GH 17276
  5030                                                   # numpy doesn't like NaN as a clip value
  5031                                                   # so ignore
  5032         1          125    125.0      0.9          if np.any(pd.isnull(lower)):
  5033         1            2      2.0      0.0              lower = None
  5034         1           30     30.0      0.2          if np.any(pd.isnull(upper)):
  5035                                                       upper = None
  5036                                           
  5037                                                   # GH 2747 (arguments were reversed)
  5038         1            2      2.0      0.0          if lower is not None and upper is not None:
  5039                                                       if is_scalar(lower) and is_scalar(upper):
  5040                                                           lower, upper = min(lower, upper), max(lower, upper)
  5041                                           
  5042                                                   # fast-path for scalars
  5043         1            2      2.0      0.0          if ((lower is None or (is_scalar(lower) and is_number(lower))) and
  5044         1           32     32.0      0.2                  (upper is None or (is_scalar(upper) and is_number(upper)))):
  5045         1        14092  14092.0     97.8              return self._clip_with_scalar(lower, upper, inplace=inplace)
  5046                                           
  5047                                                   result = self
  5048                                                   if lower is not None:
  5049                                                       result = result.clip_lower(lower, axis, inplace=inplace)
  5050                                                   if upper is not None:
  5051                                                       if inplace:
  5052                                                           result = self
  5053                                                       result = result.clip_upper(upper, axis, inplace=inplace)
  5054                                           
  5055                                                   return result

Total time: 0.00575753 s
File: pandas\core\generic.py
Function: _clip_with_scalar at line 4920

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
  4920                                               def _clip_with_scalar(self, lower, upper, inplace=False):
  4921         1            2      2.0      0.0          if ((lower is not None and np.any(isna(lower))) or
  4922         1           28     28.0      0.2                  (upper is not None and np.any(isna(upper)))):
  4923                                                       raise ValueError("Cannot use an NA value as a clip threshold")
  4924                                           
  4925         1          120    120.0      0.9          result = self.values
  4926         1         3525   3525.0     25.1          mask = isna(result)
  4927                                           
  4928         1           86     86.0      0.6          with np.errstate(all='ignore'):
  4929         1            2      2.0      0.0              if upper is not None:
  4930         1         9314   9314.0     66.4                  result = np.where(result >= upper, upper, result)
  4931         1           61     61.0      0.4              if lower is not None:
  4932                                                           result = np.where(result <= lower, lower, result)
  4933         1          283    283.0      2.0          if np.any(mask):
  4934                                                       result[mask] = np.nan
  4935                                           
  4936         1           78     78.0      0.6          axes_dict = self._construct_axes_dict()
  4937         1          532    532.0      3.8          result = self._constructor(result, **axes_dict).__finalize__(self)
  4938                                           
  4939         1            2      2.0      0.0          if inplace:
  4940                                                       self._update_inplace(result)
  4941                                                   else:
  4942         1            1      1.0      0.0              return result

Il y a encore plusieurs appels de fonction, par exemple isna et np.where, qui prennent beaucoup de temps, mais dans l'ensemble c'est au moins comparable à l' np.ndarray.clip du temps (c'est dans le régime où la différence de temps est de ~3 sur mon ordinateur).

Le traiteur doit probablement être:

  • De nombreux NumPy fonctions de délégué à une méthode de l'objet passé en, donc il peut y avoir des différences énormes quand vous passez par les différents objets.
  • Profilage, en particulier la ligne de profilage, peut être un excellent outil pour trouver les endroits où la différence de performance vient de.
  • Assurez-vous de toujours tester différemment des objets de la taille dans de tels cas. Vous pourriez être la comparaison constante des facteurs qui probablement n'a pas d'importance, sauf si vous traitez un grand nombre de petits tableaux.

Versions utilisées:

Python 3.6.3 64-bit on Windows 10
Numpy 1.13.3
Pandas 0.21.1

8voto

obgnaw Points 2489

Il suffit de lire le code source, c'est clair.

 def clip(a, a_min, a_max, out=None):
    """a : array_like Array containing elements to clip."""
    return _wrapfunc(a, 'clip', a_min, a_max, out=out)

def _wrapfunc(obj, method, *args, **kwds):
    try:
        return getattr(obj, method)(*args, **kwds)
    #This situation has occurred in the case of
    # a downstream library like 'pandas'.
    except (AttributeError, TypeError):
        return _wrapit(obj, method, *args, **kwds)

def _wrapit(obj, method, *args, **kwds):
    try:
        wrap = obj.__array_wrap__
    except AttributeError:
        wrap = None
    result = getattr(asarray(obj), method)(*args, **kwds)
    if wrap:
        if not isinstance(result, mu.ndarray):
            result = asarray(result)
        result = wrap(result)
    return result
 

rectifier:

après pandas v0.13.0_ahl1, les pandas ont leur propre implémentation de clip .

7voto

Simon Bowly Points 516

Il y a deux parties à la différence de performances pour être au courant de ici:

  • Python généraux dans chaque bibliothèque (pandas étant très utile)
  • La différence numérique de l'algorithme de mise en œuvre (pd.clip fait des appels np.where)

L'exécution de ce sur un très petit groupe doit démontrer la différence en Python frais généraux. Pour numpy, c'est naturellement très petit, cependant, les pandas ne beaucoup de vérification (les valeurs null, plus souple argument de traitement, etc), avant d'arriver à l'lourds calculs. J'ai essayé de montrer un accidenté de la répartition des étapes dont les deux codes parcourir avant de frapper le code C de la roche mère.

data = pd.Series(np.random.random(100))

Lors de l'utilisation d' np.clip sur ndarray, la charge est tout simplement le numpy fonction wrapper appeler une méthode de l'objet:

>>> %timeit np.clip(data.values, 0.2, 0.8)        # numpy wrapper, calls .clip() on the ndarray
>>> %timeit data.values.clip(0.2, 0.8)            # C function call

2.22 µs ± 125 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
1.32 µs ± 20.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Les Pandas passe plus de temps à vérifier pour les cas limites avant d'arriver à l'algorithme:

>>> %timeit np.clip(data, a_min=0.2, a_max=0.8)   # numpy wrapper, calls .clip() on the Series
>>> %timeit data.clip(lower=0.2, upper=0.8)       # pandas API method
>>> %timeit data._clip_with_scalar(0.2, 0.8)      # lowest level python function

102 µs ± 1.54 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
90.4 µs ± 1.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
73.7 µs ± 805 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Par rapport à l'ensemble des temps, la surcharge des deux bibliothèques avant de frapper le code C est assez significative. Pour numpy, l'unique habillage d'instruction prend autant de temps pour s'exécuter que le traitement numérique. Les Pandas a ~30x plus généraux que dans les deux premiers calques des appels de fonction.

Pour isoler ce qui se passe à l'algorithme de niveau, nous devons vérifier sur un plus grand tableau de référence et les mêmes fonctions:

>>> data = pd.Series(np.random.random(1000000))

>>> %timeit np.clip(data.values, 0.2, 0.8)
>>> %timeit data.values.clip(0.2, 0.8)

2.85 ms ± 37.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.85 ms ± 15.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.clip(data, a_min=0.2, a_max=0.8)
>>> %timeit data.clip(lower=0.2, upper=0.8)
>>> %timeit data._clip_with_scalar(0.2, 0.8)

12.3 ms ± 135 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
12.3 ms ± 115 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
12.2 ms ± 76.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Le python de frais généraux dans les deux cas, est maintenant négligeable; le temps pour les fonctions wrapper et la vérification argument est faible par rapport au temps de calcul sur 1 millions de valeurs. Il y a cependant un 3-4x différence de vitesse qui peut être attribuée à numérique de la mise en œuvre. En enquêtant un peu dans le code source, nous voyons que l' pandas de la mise en œuvre de l' clip utilise réellement np.where, pas np.clip:

def clip_where(data, lower, upper):
    ''' Actual implementation in pd.Series._clip_with_scalar (minus NaN handling). '''
    result = data.values
    result = np.where(result >= upper, upper, result)
    result = np.where(result <= lower, lower, result)
    return pd.Series(result)

def clip_clip(data, lower, upper):
    ''' What would happen if we used ndarray.clip instead. '''
    return pd.Series(data.values.clip(lower, upper))

L'effort supplémentaire nécessaire pour vérifier chaque condition booléenne séparément avant de faire une condition de remplacer semblerait compte de la différence de vitesse. La spécification upper et lower entraînerait 4 passe à travers le tableau numpy (deux inégalités vérifications et deux appels d' np.where). L'analyse comparative de ces deux fonctions montre que 3-4x rapport de vitesse:

>>> %timeit clip_clip(data, lower=0.2, upper=0.8)
>>> %timeit clip_where(data, lower=0.2, upper=0.8)

11.1 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.97 ms ± 76.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Je ne sais pas pourquoi les pandas devs suis allé avec cette mise en œuvre. np.clip peut être une nouvelle fonction de l'API qui auparavant nécessitaient une solution de contournement. Il est également un peu plus à ce que j'ai fait ici, depuis les pandas vérifie les différents cas avant l'exécution de l'algorithme final, et ce n'est que l'une des implémentations qui peut être appelé.

5voto

Dark Points 20515

La raison pour laquelle la performance diffère, c'est parce que numpy première tend à la recherche pour les pandas de mise en œuvre de la fonction à l'aide d' getattr que faire la même dans builtin numpy fonctions lorsqu'une pandas objet est passé.

Ce n'est pas le numpy sur les pandas de l'objet qui est lente, son les pandas version.

Lorsque vous ne

np.clip(pd.Series([1,2,3,4,5]),a_min=None,amax=1)  

_wrapfunc s'appelle :

# Code from source 
def _wrapfunc(obj, method, *args, **kwds):
    try:
        return getattr(obj, method)(*args, **kwds)

En raison d' _wrapfuncs' getattr méthode :

getattr(pd.Series([1,2,3,4,5]),'clip')(None, 1)
# Equivalent to `pd.Series([1,2,3,4,5]).clip(lower=None,upper=1)`
# 0    1
# 1    1
# 2    1
# 3    1
# 4    1
# dtype: int64

Si vous passez par les pandas de mise en œuvre il y a beaucoup de pré vérifier le travail qui est fait. Cest la raison pour laquelle les fonctions qui a les pandas de la mise en œuvre fait via numpy a une telle différence de vitesse.

Non seulement le clip, les fonctions comme cumsum,cumprod,reshape,searchsorted,transpose et beaucoup plus d'utilisations pandas version que numpy lorsque vous passez une pandas objet.

Il pourrait sembler numpy est en train de faire le travail sur ces objets, mais sous le capot de son les pandas fonction.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X