1
votes

Les pandas dupliquent les lignes en fonction de la valeur de la colonne

Étant donné le dataframe suivant

imgRaw["Category"] = ""
for index, row in df.iterrows():
    catA = row["OrderCategoryA"]
    catB = row["OrderCategoryB"]
    catC = row["OrderCategoryC"]
    catD = row["OrderCategoryD"]

    if catA == "Yes":
        row["Category"] = "OrderCategoryA"
    elif catB == "Yes":
        row["Category"] = "OrderCategoryB"
    elif catC == "Yes":
        row["Category"] = "OrderCategoryC"
    elif catD == "Yes":
        row["Category"] = "OrderCategoryD"

Comment puis-je transformer cela pour créer des lignes basées sur la OrderCategory?

OrderCategoryA     No  Yes
Cust_ID                   
1               0   1    0
2               0   1    0
3               0   1    0
4               1   0    0
5               1   0    0
6               0   1    0
7               0   1    0
8               0   1    0
9               1   0    0
10              0   0    1

J'ai essayé d'utiliser tableau croisé pour commencer avec une OrderCategory , et j'ai prévu de dupliquer pour chaque catégorie, mais cela semble inefficace et je ne savais pas trop comment procéder obtenir le résultat souhaité ...

imgCROSS = pd.crosstab(df["Cust_ID"], df["OrderCategoryA"])

Renvoie ...

+--------+-----------+----------+----------------+
|Cust_ID | OrderMade |OrderType | OrderCategory  |
|--------+-----------+----------+----------------|
|1       |   Yes     |    A     | OrderCategoryB |
|2       |   Yes     |    A     | OrderCategoryC |
|3       |   Yes     |    B     | OrderCategoryC |
|4       |   No      |          |                |
|5       |   No      |          |                |
|6       |   Yes     |    C     | OrderCategoryC |
|6       |   Yes     |    C     | OrderCategoryD |
|7       |   Yes     |    A     | OrderCategoryB |
|8       |   Yes     |    A     | OrderCategoryC |
|9       |   No      |          |                |
|10      |   Yes     |    B     | OrderCategoryA |
|10      |   Yes     |    B     | OrderCategoryB |
+--------+-----------+----------+----------------+

J'ai aussi pensé que je pourrais remplir un nouveau vide colonne appelée Catégorie et itérer sur chaque ligne, en remplissant la catégorie appropriée en fonction de la valeur Oui / Non , mais cela ne fonctionnerait pas pour les lignes qui ont plusieurs catégories. De plus, l'implémentation ci-dessous de cette idée a renvoyé une colonne vide.

data = [[1, 'Yes','A','No','Yes','No','No','No'],
        [2, 'Yes','A','No','No','Yes','No','No'],
        [3, 'Yes','B','No','No','Yes','No','No'],
        [4, 'No','','','','','',''],
        [5, 'No','','','','','',''],
        [6, 'Yes','C','No','No','Yes','Yes','No'],
        [7, 'Yes','A','No','Yes','No','No','No'],
        [8, 'Yes','A','No','No','Yes','No','No'],
        [9, 'No','','','','','',''],
        [10, 'Yes','B','Yes','Yes','No','No','No']]
df = pd.DataFrame(data,columns=['Cust_ID','OrderMade','OrderType','OrderCategoryA','OrderCategoryB','OrderCategoryC','OrderCategoryD'])


+----+-----------+-------------+-------------+------------------+------------------+------------------+------------------+
|    |   Cust_ID | OrderMade   | OrderType   | OrderCategoryA   | OrderCategoryB   | OrderCategoryC   | OrderCategoryD   |
|----+-----------+-------------+-------------+------------------+------------------+------------------+------------------|
|  0 |         1 | Yes         | A           | No               | Yes              | No               | No               |
|  1 |         2 | Yes         | A           | No               | No               | Yes              | No               |
|  2 |         3 | Yes         | B           | No               | No               | Yes              | No               |
|  3 |         4 | No          |             |                  |                  |                  |                  |
|  4 |         5 | No          |             |                  |                  |                  |                  |
|  5 |         6 | Yes         | C           | No               | No               | Yes              | Yes              |
|  6 |         7 | Yes         | A           | No               | Yes              | No               | No               |
|  7 |         8 | Yes         | A           | No               | No               | Yes              | No               |
|  8 |         9 | No          |             |                  |                  |                  |                  |
|  9 |        10 | Yes         | B           | Yes              | Yes              | No               | No               |
+----+-----------+-------------+-------------+------------------+------------------+------------------+------------------+

Je sais que je dois transformer le dataframe, probablement plusieurs fois avant de pouvoir obtenir le résultat souhaité. Je ne sais pas comment procéder.


0 commentaires

4 Réponses :


0
votes

Comme suggéré dans l'autre réponse, vous voulez fondre avec un nettoyage supplémentaire, et fusionner:

    Cust_ID OrderMade OrderType   OrderCategory
0        10       Yes         B  OrderCategoryA
1        10       Yes         B  OrderCategoryB
2         1       Yes         A  OrderCategoryB
3         7       Yes         A  OrderCategoryB
4         2       Yes         A  OrderCategoryC
5         3       Yes         B  OrderCategoryC
6         6       Yes         C  OrderCategoryC
7         6       Yes         C  OrderCategoryD
8         8       Yes         A  OrderCategoryC
9         4        No                       NaN
10        5        No                       NaN
11        9        No                       NaN

Sortie:

id_cols = ['Cust_ID','OrderMade','OrderType']
new_df = df[df.OrderMade.eq('Yes')].melt(id_vars=id_cols, var_name='OrderCategory')


new_df[new_df['value'].ne('No')]
        .merge(df.loc[df.OrderMade.eq('No'), 
                      ['Cust_ID','OrderMade','OrderType']],
               how='outer')
        .drop('value',axis=1)


0 commentaires

1
votes

Voici une façon de le faire (j'ai dû modifier votre dataframe d'origine pour qu'il n'ait qu'une seule OrderCategoryD au lieu de deux ... j'espère que c'était une faute de frappe):

keep_cols = ['Cust_ID','OrderMade','OrderType']
build = pd.DataFrame()

for col in df.columns:
   if 'OrderCategory' in col:
     cat = col[-1:]                              # Get the category letter
     temp = df.loc[df[col] == 'Yes', keep_cols]  # Get all the rows with a yes in this column
     temp['OrderCategory'] = cat                 # Append a column with the correct letter
     build = build.append(temp)                  # Append that df to our new df

# Once that's done, get all the rows that have a 'No' in the OrderMade column
final = pd.merge(build, df[keep_cols], how='right').sort_values('Cust_ID')
final = final.reset_index().drop(columns=['index'])

p >


0 commentaires

1
votes

Ajouter une autre colonne de catégorie représentant les 'Non' dans 'OrderMade'

Cela généralise le problème et nous permet d'utiliser une méthode plus uniforme. p>

d = df.assign(**{'': df.OrderMade.map({'Yes': 'No', 'No': 'Yes'})})
d.melt(['Cust_ID', 'OrderMade', 'OrderType'], var_name='OrderCategory') \
 .query('value == "Yes"').drop('value', 1).sort_values('Cust_ID')

    Cust_ID OrderMade OrderType   OrderCategory
10        1       Yes         A  OrderCategoryB
21        2       Yes         A  OrderCategoryC
22        3       Yes         B  OrderCategoryC
53        4        No                          
54        5        No                          
25        6       Yes         C  OrderCategoryC
35        6       Yes         C  OrderCategoryD
16        7       Yes         A  OrderCategoryB
27        8       Yes         A  OrderCategoryC
58        9        No                          
9        10       Yes         B  OrderCategoryA
19       10       Yes         B  OrderCategoryB

melt

L'ajout de la colonne simplifie également la fusion

d = df.assign(**{'': df.OrderMade.map({'Yes': 'No', 'No': 'Yes'})})
ids, cat = np.split(d, [3], 1)  # split between 3rd and 4th columns
i, j = np.where(cat.eq('Yes'))

ids.iloc[i].assign(OrderCategory=cat.columns[j])

  Cust_ID OrderMade OrderType   OrderCategory
0       1       Yes         A  OrderCategoryB
1       2       Yes         A  OrderCategoryC
2       3       Yes         B  OrderCategoryC
3       4        No                          
4       5        No                          
5       6       Yes         C  OrderCategoryC
5       6       Yes         C  OrderCategoryD
6       7       Yes         A  OrderCategoryB
7       8       Yes         A  OrderCategoryC
8       9        No                          
9      10       Yes         B  OrderCategoryA
9      10       Yes         B  OrderCategoryB

h3>


0 commentaires

3
votes

Utilisons les pandas en quatre étapes:

    Cust_ID OrderMade OrderType   OrderCategory
0         1       Yes         A  OrderCategoryB
1         2       Yes         A  OrderCategoryC
2         3       Yes         B  OrderCategoryC
3         4        No                          
8         5        No                          
13        6       Yes         C  OrderCategoryC
14        6       Yes         C  OrderCategoryD
15        7       Yes         A  OrderCategoryB
16        8       Yes         A  OrderCategoryC
17        9        No                          
22       10       Yes         B  OrderCategoryA
23       10       Yes         B  OrderCategoryB

Sortie:

df_1 = df.set_index(['Cust_ID', 'OrderMade', 'OrderType'])

df_2 = df_1.where((df_1 == "Yes") | (df_1 == "")).rename_axis('OrderCategory', axis=1).stack().reset_index()

df_2['OrderCategory'] = df_2['OrderCategory'].mask(df_2['OrderMade'] == 'No','')

df_2.drop_duplicates().drop(0, axis=1)


0 commentaires