1
votes

Construire un bloc de données à l'aide de groupby

Mon dataframe ressemble à ceci:

df.groupby(['date','id'])['id'].unique().value_counts()
df.groupby(['date','id'])['id'].count()

Je voudrais l'écrire avec les colonnes 'target', 'source', 'weights', où: «cible» et «source» sont toutes deux des «identifiants» et «pondérations» compte sur le nombre de jours pendant lesquels le prix «cible» et «source» a changé simultanément. Cela ressemblerait donc à:

target  source  weights
60059   60060   2
60059   60076   1   
60060   60076   1

Mon objectif est d'utiliser ce dataframe pour créer un graphique networkx.

J'ai essayé d'utiliser groupby

                date    id     pct_change
12355258    2010-07-28  60059   0.210210
12355265    2010-07-28  60060   0.592000
12355282    2010-07-29  60059   0.300273
12355307    2010-07-29  60060   0.481982
12355330    2010-07-28  60076   0.400729

et les boucles for (qui étaient terribles).

J'ai l'impression de manquer un petit pas dans le groupby, mais je ne pourrais pas dire ce qui manquait.

Merci pour votre aide.


8 commentaires

12177535 lignes, 24800 identifiants individuels


Le problème avec autant d'identifiants individuels si vous voulez toutes les combinaisons de 2 identifiants, vous obtenez quelque chose comme 300 millions de lignes et j'ai le sentiment que les pandas auront du mal à effectuer des opérations comme celle-ci, même en stockant le résultat avec une mémoire informatique "normale" c'est probablement trop


J'ai une variable alternative dans l'ensemble de données qui a 26 catégories au lieu de 24800. Serait-ce faisable?


Merci beaucoup!


désolé de déranger à nouveau ... sauriez-vous un moyen de le faire pour les «id»? Avec la variable «catégorie», le réseau était entièrement connecté et je ne peux pas faire une analyse plus approfondie.


cela a du sens avec seulement 26 catégories au lieu de 25K identifiants, en particulier avec autant de dates, vous obtenez un réseau entièrement connecté. honnêtement, je ne suis pas sûr que ce soit possible avec autant de données sur un ordinateur. Pardon


Merci quand même! Je pensais qu'il serait peut-être possible de faire la même chose que vous avez fait sur pyspark, mais je ne suis pas sûr.


peut être. J'en ai fait avant et je sais que le pivot existe, mais le reste je ne suis pas sûr.


4 Réponses :


2
votes

L'idée est d'utiliser d'abord un pivto_table pour obtenir True si l'id a un pct_change pour chaque date

# get all combinations of ids
from itertools import combinations
a, b = map(list, zip(*combinations(df_.index, 2)))

res = (pd.DataFrame({'target':a, 'source':b})
         .assign(weigths=(df_.loc[a].to_numpy()
                          &df_.loc[b].to_numpy()
                         ).sum(axis=1))
      )
print(res)
   target  source  weigths
0   60059   60060        2
1   60059   60076        1
2   60060   60076        1

Ensuite, vous pouvez utiliser combinaison de itertools pour créer toutes les paires possibles, utilisez-les pour sélectionner les lignes et utilisez l'opérateur & pour voir où les deux ont Vrai à la même date, additionnez le long de la colonnes (récupérez la colonne des poids). Attribuez cette colonne au Dataframe créé à partir des deux listes de combinaisons.

#first pivot to get True if any value of id for a date
df_ = df.pivot_table(index='id', columns='date', values='pct_change', 
                     aggfunc=any, fill_value=False)
print(df_)
date  2010-07-28 2010-07-29
id                         
60059       True       True
60060       True       True
60076       True      False

Remarque: n'oubliez pas de changer le index = 'id' dans le pivot_table avec le nom de votre colonne catégorielle, sinon il y a de fortes chances que votre ordinateur ne puisse pas gérer les opérations suivantes et planter


0 commentaires

1
votes

essayez ceci

import pandas as pd, numpy as np

ids = df.id.unique()
WeightDf = pd.DataFrame(index=ids, columns=ids)
WeightDf.loc[:, :] = 0

def weigh(ID):
    IdDates =  set(df.loc[df.id==ID].date.to_list())
    for i in ids:
        WeightDf.at[ID, i] = len(set.intersection(set(df.loc[df.id==i].date.to_list()), IdDates))
        
pd.Series(ids).apply(weigh)
print(WeightDf)

import itertools as itt
result = pd.DataFrame(columns=['Id1', 'Id2', 'Weight'])
for i1, i2 in itt.combinations(ids, 2):
    result = pd.concat([result, pd.DataFrame(data=[{'Id1':i1, 'Id2':i2,'Weight':WeightDf.loc[i1, i2]}])])

print(result)


3 commentaires

Cela crée une matrice, pas vraiment les colonnes cible, source, pondérations.


Merci! Je me demandais s'il serait possible de le faire avec la variable «id» qui a 24800 observations? Je ne sais pas si l'utilisation de combinaisons est possible avec un si grand nombre. Pensez-vous que ce soit le cas?


Ce sera possible parce que, vous pourriez avoir 24800 observations, mais, vous aurez moins d ' IDS uniques



0
votes

Vu de nombreuses variantes de ce cas d'utilisation - génération de combinaisons

 weights  target  source
       2   60059   60060
       1   60059   60076
       1   60060   60076

sortie

import itertools

df = pd.read_csv(io.StringIO("""                date    id     pct_change
12355258    2010-07-28  60059   0.210210
12355265    2010-07-28  60060   0.592000
12355282    2010-07-29  60059   0.300273
12355307    2010-07-29  60060   0.481982
12355330    2010-07-28  60076   0.400729"""), sep="\s+")

# generate combinations of two... edge case when a group has only one member
# tuple of itself to itself
dfx = (df.groupby('date').agg({"id": lambda s: list(itertools.combinations(list(s), 2))
                               if len(list(s))>1 else [tuple(list(s)*2)]})
    .explode("id")
     .groupby("id").agg({"id":"count"})
     .rename(columns={"id":"weights"})
     .reset_index()
     .assign(target=lambda dfa: dfa["id"].apply(lambda s: s[0]),
           source=lambda dfa: dfa["id"].apply(lambda s: s[1]))
     .drop(columns="id")
)

print(dfx.to_string(index=False))


0 commentaires