Un paramètre étiqueté « poids » dans la méthode df.mean serait extrêmement utile. Dans numpy, cette fonctionnalité est fournie via np.average au lieu de np.mean, ce qui, je suppose, est la manière dont des fonctionnalités similaires seraient ajoutées aux pandas.
ex fonctionnalité demandée :
> a = np.array([[2,4,6],[8,10,12]])
> w = [.5, 0, .5]
> np_avg = np.average(a, weights = w, axis=1)
#output -> array([ 4., 10.])
> pd_avg = pandas.DataFrame(a).mean(weights = w, axis=1)
#desired output -> series with entries 4 and 10
S'il s'agit d'une fonctionnalité souhaitée, je la compléterai et soumettrai une demande d'extraction si vous le souhaitez. Si c'est déjà quelque part que j'ai oublié, mes excuses, j'ai essayé de regarder attentivement.
Pourquoi ne pas simplement écrire :
pd_avg = (np.array(w) * pandas.DataFrame(a)).mean(axis=1)
Je suis d'accord avec @bgrayburn , les statistiques pondérées seraient très utiles chez les pandas. On peut utiliser statsmodel mais étendre les méthodes DataFrame pour utiliser le poids serait très utile pour les personnes utilisant des données d'enquête pondérées.
@Stephan : Je suis d'accord que votre extrait de code accomplit (presque) la même chose
d'un point de vue fonctionnel, mais du point de vue de la lisibilité du code, et
un point de vue de réutilisation de code, incluant un paramètre de poids semble optimal. Aussi
le paramètre de poids de numpy normalise automatiquement le vecteur de poids qui
est aussi extrêmement utile.
Le samedi 2 mai 2015 à 15h04, Mahdi Ben Jelloul [email protected]
a écrit:
Je suis d'accord avec @bgrayburn https://github.com/bgrayburn , pondéré
les statistiques seraient très utiles chez les pandas. On peut utiliser statsmodel mais
étendre les méthodes DataFrame pour utiliser le poids serait très utile pour les personnes
en utilisant des données d'enquête pondérées.-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/pydata/pandas/issues/10030#issuecomment -98386585.
D'accord, assez juste. Cela semble à la portée des pandas. Nous avons récemment ajouté une méthode sample
qui inclut un argument similaire weights
(#9666) qui pourrait être utile comme point de départ.
@shoyer a demandé ailleurs (#10000) une liste de méthodes qui pourraient être améliorées par une version « pondérée ».
Presque toutes les fonctions statistiques que l'on peut trouver ici sont candidates. Je peux aussi penser aux calculs de describe, value_counts, qcut, hist et margin dans les tableaux croisés dynamiques.
Encore une fois, je pense que nous sommes ouverts à la plupart de ces changements (qui sont tous rétrocompatibles avec weights=None
). Le principal obstacle est que nous avons besoin d'implémentations, de documentation et de références pour montrer que nous ne ralentissons rien. Les PR seraient les bienvenus. Cependant, il vaudrait également la peine de vérifier si l'un d'entre eux pourrait être poussé en amont vers numpy.
@shoyer @benjello désolé pour le retard à ce sujet, je prévois toujours de soumettre un PR, je vais coder ce week-end.
en ce qui concerne la chose numpy, pour les moyens pondérés, ils utilisent .average que vous pouvez voir ici . Mon plan était de mettre en œuvre
pd_avg = (np.array(w) * pandas.DataFrame(a)).mean(axis=1)
à peu près comme écrit, en multipliant les colonnes de la trame de données d'entrée par le vecteur de poids. Alternativement, nous pourrions appeler np.average lorsqu'un paramètre de poids est présent, OU (3ème option) nous pourrions implémenter un pandas.DataFrame(a).average(weights=[...]) pour refléter les pandas.
une dernière question, la pondération doit-elle être applicable en mode axe=0 ou axe=1 ? Je suppose que oui, mais je voulais vérifier.
Faites-moi savoir vos préférences, ou si d'une manière ou d'une autre cela devrait être incorporé avec un changement plus important comme mentionné ci-dessus.
Meilleur
pourquoi n'ajoutez-vous pas simplement un mot-clé de poids à .mean ?
beaucoup plus cohérent dans l'API et nous n'implémentons pas la moyenne, je suppose, car c'est juste déroutant
et cela doit se répercuter sur nanops.py où tout le calcul réel est effectué - cela gère de nombreux types différents
@bgrayburn : je pense que la suggestion de suivie : utiliser une moyenne avec des poids est ce à quoi vous vous attendez pour une moyenne pondérée
+1 rendrait le travail avec des échantillons de microdonnées (pensez PUMS ) tellement plus agréable.
Je suis d'accord qu'il serait préférable d'incorporer cela directement dans des fonctions d'agrégation comme mean
et var
au lieu d'ajouter des méthodes spécialisées comme average
.
+1 à @mattayes & @shoyer -- lorsque vous travaillez avec des données pondérées, vous voulez à peu près pondérer CHAQUE statistique et graphique que vous générez. C'est à peu près une nécessité d'avoir une option de pondération si vous travaillez avec de telles données.
L'ajout d'un argument de poids à autant de fonctions que possible au fil du temps semble être la voie à suivre dans la mesure où il ne sera pas géré dans numpy/statsmodels/matplotlib.
Stata, par exemple, permet une option de poids pour pratiquement toutes les fonctions. J'utilise très fréquemment le tabstat de stata avec l'option de poids et pour le moment, il n'y a pas de bon analogue chez les pandas à ma connaissance.
Une complication possible à considérer : il existe potentiellement différents types de poids. Stata, par exemple, définit 4 types de poids : fréquence, analytique, probabilité et importance (bien que le dernier ne soit qu'un fourre-tout abstrait). [http://www.stata.com/help.cgi?weight]
Je pense que dans ce fil, la plupart des gens pensent aux poids de fréquence, mais il pourrait être nécessaire de clarifier cela. En outre, cela n'aura probablement pas d'importance pour quelque chose comme la moyenne ou la médiane, mais cela pourrait affecter quelque chose comme la variance.
Il y a eu des discussions récentes sur la mise en œuvre d'algorithmes efficaces pour la partition pondérée (par exemple, pour faire une médiane pondérée) en amont dans NumPy, ainsi :
https://mail.scipy.org/pipermail/numpy-discussion/2016-February/075000.html
Dans tous les cas, une première ébauche qui utilise le tri pour faire une médiane pondérée serait toujours utile.
Depuis https://github.com/pydata/xarray/pull/650 :
Que diriez-vous de concevoir cela comme une interface de type groupby
? De la même manière que .rolling
(ou .expanding
& .ewm
chez les pandas) ?
Ainsi par exemple ds.weighted(weights=ds.dim).mean()
.
Et puis c'est extensible, propre, pandan-tic.
que feriez-vous d'autre avec une interface .weighted(..).mean() ?
IOW quels autres paramètres accepterait-il en dehors des poids réels ?
@jreback Je pense que .weighted()
n'accepterait que weights
, qui pourrait être soit un tableau, soit un appelable de la forme habituelle ( lambda df: ....
). Mais la classe WeightedMethods
pourrait également exposer des implémentations pondérées d'autres méthodes, telles que std, var, median, sum, value_counts, hist, etc. J'envisagerais même de passer à sample
et de déprécier le weights
argumentation.
@shoyer, je peux voir une syntaxe à partir de cela, par exemple
df.weighted('A').B.mean()
est assez clair
bien que df.B.mean(weights=df.A)
soit tout aussi clair, alors cherchez un cas où cela est nettement plus agréable.
une idée de comment/fait R faire ça ? (Julia?)
J'ai utilisé R wtd.stats .
wtd.mean(x, weights=NULL, normwt="ignored", na.rm=TRUE)
wtd.var(x, weights=NULL, normwt=FALSE, na.rm=TRUE)
wtd.quantile(x, weights=NULL, probs=c(0, .25, .5, .75, 1),
type=c('quantile','(i-1)/(n-1)','i/(n+1)','i/n'),
normwt=FALSE, na.rm=TRUE)
wtd.Ecdf(x, weights=NULL,
type=c('i/n','(i-1)/(n-1)','i/(n+1)'),
normwt=FALSE, na.rm=TRUE)
wtd.table(x, weights=NULL, type=c('list','table'),
normwt=FALSE, na.rm=TRUE)
wtd.rank(x, weights=NULL, normwt=FALSE, na.rm=TRUE)
wtd.loess.noiter(x, y, weights=rep(1,n), robust=rep(1,n),
span=2/3, degree=1, cell=.13333,
type=c('all','ordered all','evaluate'),
evaluation=100, na.rm=TRUE)
@benjello hmm, c'est intéressant.
bien que df.B.mean(weights=df.A) soit tout aussi clair, alors recherchez un cas où cela est nettement plus agréable.
À première vue, cela a l'air aussi bien. Mais du point de vue de la conception de l'API, l'ajout d'un argument de mot-clé pour weights
est beaucoup moins élégant.
Le fait d'être « pondéré » est orthogonal au type de calcul statistique. Avec cette proposition, au lieu d'ajouter l'argument mot-clé weights
à des N
différentes méthodes, nous définissons une seule méthode weighted
et y ajoutons des méthodes statistiques qui correspondent exactement à la signature de les mêmes méthodes sur DataFrame/Series. Cela montre clairement que toutes ces méthodes partagent la même approche et empêchent les signatures de méthode de développer des arguments supplémentaires qui déclenchent des chemins de code entièrement indépendants (ce qui est un signe d'odeur de code).
Séparément : peut-être que weightby
est un nom légèrement meilleur que weighted
? Cela suggère plus de similitude avec groupby
.
ouais je pense que je pourrais me remettre de .weightby(...)
. Ce n'est pas si difficile car la plupart des machines existent déjà...... alors si quelqu'un veut faire un prototype...!
Le fait d'être « pondéré » est orthogonal au type de calcul statistique.
Pouvez-vous développer?
Comment définissons-nous les poids ici ? Les poids négatifs sont-ils acceptés (je pense que le domaine pourrait dépendre de la fonction) ? Les poids doivent-ils être de la même longueur que les points de données ? Cette API effectue-t-elle une validation ou est-elle différée vers les fonctions de statistiques (si les poids peuvent être un lambda
, j'imagine que c'est le dernier) ?
Comment définissons-nous les poids ici ? Les poids négatifs sont-ils acceptés (je pense que le domaine pourrait dépendre de la fonction) ? Les poids doivent-ils être de la même longueur que les points de données ?
Ma vision de l'argument weights
est assez similaire à la façon dont il est actuellement défini sur DataFrame.sample
:
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.sample.html
Les poids doivent donc être des nombres non négatifs pouvant être alignés avec l'objet pondéré. Ils n'ont pas besoin d'être prénormalisés, bien qu'il devrait peut-être y avoir un argument de mot-clé sur weightby
pour indiquer que cela n'a pas besoin d'être vérifié (s'il s'agit en fait d'un réel problème de performances). Nous pouvons également ajouter un argument axis
, qui vous permet de sélectionner l'axe sur lequel les poids sont appliqués. Cela permettrait par exemple de pondérer différemment les colonnes d'un DataFrame.
Cette API effectue-t-elle une validation ou est-elle différée vers les fonctions de statistiques (si les poids peuvent être un lambda, j'imagine que c'est ce dernier) ?
Idéalement, nous pourrions partager la validation entre différentes méthodes sur weightby
. Je ne pense pas qu'il soit particulièrement important de valider avant ou après l'appel de la sous-méthode sur l'objet WeightByMethods
.
Je vois. Ça a du sens. Les endroits où j'ai rencontré des « pondérations » négatives incluent le calcul des rendements du portefeuille (qui est une combinaison linéaire, mais pas nécessairement convexe, des rendements des titres individuels), mais ce type de « pondérations » est bien sûr moins dense.
salut les gars,
Je pense que c'est une excellente idée! Cependant, quand on essaie de regarder plus profondément, les choses ne sont pas aussi simples que prévu. Par exemple, ce sont les types de poids utilisés dans Stata
1. fweights, or frequency weights, are weights that indicate the number of duplicated observations.
2. pweights, or sampling weights, are weights that denote the inverse of the probability that the
observation is included because of the sampling design.
3. aweights, or analytic weights, are weights that are inversely proportional to the variance of an
observation; that is, the variance of the jth observation is assumed to be sigma^2/w_j, where w_j are
the weights. Typically, the observations represent averages and the weights are the number of elements
that gave rise to the average. For most Stata commands, the recorded scale of aweights is irrelevant;
Stata internally rescales them to sum to N, the number of observations in your data, when it uses them.
4. iweights, or importance weights, are weights that indicate the "importance" of the observation in some
vague sense. iweights have no formal statistical definition; any command that supports iweights will
define exactly how they are treated. Usually, they are intended for use by programmers who want to
produce a certain computation.
regardez aussi ici une comparaison plus approfondie http://www.ats.ucla.edu/stat/sas/faq/weights.htm
L'essentiel est : cela vaut vraiment la peine d'avoir une bonne discussion sur les poids avant de les inclure.
Voulez-vous être super flexible et mettre la responsabilité sur l'utilisateur ? Ou voulez-vous avoir des poids non négatifs simples et clairs (d'où une forte limitation sur ce qui peut être fait ?)
Tout dépend du nombre de mots-clés que vous souhaitez ajouter à la fonction pd.weight
.
@randomgambit eh bien, un problème beaucoup plus important est que quelqu'un se porte volontaire pour mettre cela en œuvre. Avoir la discussion, c'est bien. Mais quelqu'un devrait intervenir.
@Jeff Reback, ouais désolé, j'ai dit que je le ferais il y a un bon moment, mais la vie est
a eu raison de moi. Je le ferais toujours si je peux trouver le temps, mais n'importe qui
autrement disposé devrait certainement faire avancer les choses.
@bgrayburn haha, t'en veux pas :>
hehe assez juste @jreback , mais comme je vous l'ai dit, je n'ai pas les compétences pour aider à coder. Je ne peux partager mes remarques (je suis un doctorat en économie qui fait de la recherche universitaire) que si vous pensez que c'est utile.
Je ne suis peut-être pas la meilleure personne pour essayer de résoudre ce problème - mais je suis particulièrement intéressé à avoir une moyenne pondérée pour le groupby. Spécifiquement pour qu'il puisse être inclus dans un dictionnaire groupby().agg(). Je ne veux pas dupliquer ce que quelqu'un d'autre a fait à ce sujet. Donc, si quelqu'un peut m'indiquer d'autres ressources ou parties spécifiques du code où je peux commencer à regarder cela, je lui en serais reconnaissant.
Un problème à considérer est que .weightedby()
peut devoir être combiné avec .resample()
, .groupby()
et des amis : data.resample("1D").weightedby("count")["size"].mean()
, data.groupby("category").weightedby("count")["size"].mean()
.
ok j'ai une implémentation ici
Fonctionne bien (je n'ai pas encore complètement testé l'API, mais .sum()/.mean() ok pour l'instant).
In [1]: df = DataFrame({'A': [1, 2, 3, 4],
...: 'B': [1, 2, 3, 4]})
In [2]: df
Out[2]:
A B
0 1 1
1 2 2
2 3 3
3 4 4
In [3]: w = df.weightby('A')
In [4]: w.sum()
Out[4]:
B 3.0
dtype: float64
In [5]: w.mean()
Out[5]:
B 0.75
dtype: float64
In [6]: w._weights
Out[6]: array([ 0.1, 0.2, 0.3, 0.4])
In [7]: w.sample(2)
Out[7]:
B
2 3
1 2
cc @josef-pkt
donc je fais juste values * normalized_weights
puis je cours à travers nos routines pour sum/mean.
que diriez-vous de moments plus élevés, var
, skew
, kurt
?
( @jreback désolé d'être en retard, j'ai déjà écrit ceci il y a quelques jours mais j'ai oublié d'appuyer sur « commentaire », et maintenant je vois que vous avez ouvert un PR)
Personnellement, je trouve la syntaxe weightby
un peu maladroite, et je pense qu'un argument weights=
peut être plus facile à comprendre pour les utilisateurs. Mais je vois qu'il y avait quelques défenseurs de cette syntaxe ci-dessus.
Je trouve également que la "référence" (en tant qu'API similaire) à groupby/resample/rolling n'est pas vraiment un plus. Dans mon esprit, il s'agit d'un concept totalement différent de groupby/resample/rolling, qui consiste à diviser les données en groupes, à appliquer une fonction et à réassembler les résultats dans une nouvelle trame (comment est divisé et s'il y a chevauchement entre les groupes, bien sûr, diffère selon les méthodes).
Bonjour! travail incroyable de @jreback comme toujours. Pour ce que ça vaut (c'est-à-dire au cas où vous vous ficheriez de mon conseil), je suis également d'accord avec @jorisvandenbossche pour
Le concept de pondération est _dépendant du calcul_. Vous souhaiterez peut-être obtenir une moyenne pondérée, mais aussi une somme sans pondération.
Donc quelque chose comme
df.sum(weight = df.myweights)
ou df.groupby('jeff').mean(weights = df.myweights)
me semble plus naturel.
J'ai réalisé que j'avais commenté le PR (mais mis ci-dessous aussi)
voir ici : https://github.com/pandas-dev/pandas/pull/15031#issuecomment -269992903
Il est assez simple de passer à une API comme la suivante (en fait, l'implémentation réelle est déjà comme celle-ci, mais je passe un kwargs caché _weights
).
df.sum(...., weights=...)
df.groupby(...).sum(...., weights=...)
df.sample(....weights=...)
(reste le même)
J'arracherais le code de validation de poids et le mettrais ailleurs (en tant que fonction).
Le seul inconvénient de cela, il serait difficile de spécifier ensuite différents types de poids (comme le lien dans la section supérieure), car nous pourrions alors avoir besoin d'ajouter d'autres kwargs comme iweights, aweights
etc. .
Package intéressant traitant des calculs pondérés ici - https://github.com/jsvine/weightedcalcs. Possède une API du type weightby
.
Je recherche des moyennes pondérées en groupe par agrégats, où les poids sont une autre colonne de la base de données. Ce fil inclut-il un support pour cela? Pour expliciter ma question :
lcc:
snid mjd band flux weights
obsHistID
609734 141374000 60493.416959 lsstg 2.825651e-09 6.442312e+20
609733 141374000 60493.416511 lsstg 2.893961e-09 5.962141e+20
609732 141374000 60493.416062 lsstg 2.834461e-09 6.590458e+20
....
611542 141374000 60495.426047 lssti 6.722778e-09 1.307280e+20
610790 141374000 60494.432074 lsstz 6.619978e-09 6.156260e+19
et je fais des opérations comme :
grouped = lcc.groupby(['snid', 'band', 'night'])
res = grouped.agg(dict(flux=np.mean))
Ce que je veux vraiment faire, c'est rapide
res = grouped.agg(dict(flux=weightedmean(weights='weights'))
Le vrai problème est que cela nécessite deux colonnes dans l'entrée agrégée. J'ai examiné des solutions de contournement comme celles suggérées ici, mais je trouve que cela est lent :
Dans mon cas, lorsque la trame de données d'origine contient environ 10 000 lignes et 10 000 groupes, un np.mean
direct de
Même problème ici.
Pour le moment, j'utilise .apply comme solution de contournement, mais c'est lent.
Pouvoir appeler .agg sur des objets groupby en utilisant plus d'une colonne en entrée serait très apprécié.
Vous pouvez améliorer considérablement les performances en n'utilisant pas apply, mais en créant le calcul à partir des opérations vectorisées existantes. Exemple de moyenne ci-dessous.
In [49]: df = pd.DataFrame({'id': np.repeat(np.arange(100, dtype='i8'), 100),
...: 'v': np.random.randn(10000),
...: 'w': np.random.randn(10000)})
In [46]: %timeit mean1 = df.groupby('id').apply(lambda x: (x['v'] * x['w']).sum() / x['w'].sum())
32.4 ms ± 2.25 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [47]: %%timeit
...: df['interim'] = df['v'] * df['w']
...: gb = df.groupby('id')
...: mean2 = gb['interim'].sum() / gb['w'].sum()
1.21 ms ± 16.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [48]: np.allclose(mean1, mean2)
Out[48]: True
C'est tellement compliqué entre les types de poids, l'API, etc. mais juste pour intervenir pendant que les choses sont fraîches dans mon esprit :
D'un point de vue statistique, les statistiques de base semblent se diviser en 2 catégories :
Je pense que la première catégorie est très simple à gérer. Je suis au mieux un statisticien amateur, mais je pense qu'il n'y a vraiment qu'une seule méthode de base pour calculer la moyenne/max/médiane, etc.
Inversement, std dev & variance sont beaucoup plus compliqués que vous ne le pensez - non pas que les mathématiques soient si difficiles, mais plus que "std dev" peut signifier plus d'une chose ici. Vraiment un excellent article ici qui expose les problèmes:
https://www.stata.com/support/faqs/statistics/weights-and-summary-statistics/
Par exemple, si vous saisissez ces deux commandes dans stata :
sum x [fw=weight], detail
sum x [aw=weight], detail
vous obtiendrez les mêmes résultats pour toutes les statistiques sauf std et var
De plus, dans la mesure où les pandas transmettent ce genre de chose aux modèles de statistiques, ils ont ici une bibliothèque qui pondère la plupart des statistiques de base (bien que les valeurs min et max semblent manquantes). Voir ce lien pour en savoir plus (une réponse récente que j'ai écrite à SO en utilisant la bibliothèque statsmodel):
regardez, je suis désolé mais c'est en grande partie incorrect. les statistiques pondérées sont vraiment des trucs de base
@randomgambit
OK, alors laquelle de ces réponses est correcte ?
sum x [fw=weight], detail
sum x [aw=weight], detail
FWI :
J'avais besoin d'un quantile pondéré rééchantillonné et je l'ai mis en œuvre comme suit.
def resample_weighted_quantile(frame, weight=None, rule='D', q=0.5):
if weight is None:
return frame.resample(rule).apply(lambda x: x.quantile(q))
else:
data = [series.resample(rule).apply(_weighted_quantile, weight=weight[col], q=q).rename(col)
for col, series in frame.items()]
return pd.concat(data, axis=1)
def _weighted_quantile(series, weight, q):
series = series.sort_values()
cumsum = weight.reindex(series.index).cumsum()
cutoff = cumsum.iloc[-1] * q
return series[cumsum >= cutoff].iloc[0]
frame et weight sont des dataframes avec le même index et les mêmes colonnes. Cela pourrait probablement être optimisé, mais au moins cela fonctionne.
Ce serait un excellent ajout aux pandas!
Commentaire le plus utile
Je suis d'accord avec @bgrayburn , les statistiques pondérées seraient très utiles chez les pandas. On peut utiliser statsmodel mais étendre les méthodes DataFrame pour utiliser le poids serait très utile pour les personnes utilisant des données d'enquête pondérées.