2017-10-20 26 views
2

J'ai une base de données telle que la colonne contient à la fois des objets json et des chaînes. Je veux me débarrasser des lignes qui ne contiennent pas d'objets JSON.Déposer des lignes d'objet non-json à partir d'une colonne de données Python

Ci-dessous est la façon dont mon dataframe ressemble à:

import pandas as pd 

df = pd.DataFrame({'A': ["hello","world",{"a":5,"b":6,"c":8},"usa","india",{"a":9,"b":10,"c":11}]}) 

print(df) 

Comment dois-je supprimer les lignes qui ne contient que des chaînes, de sorte qu'après la suppression de ces lignes de cordes, je peux appliquer ci-dessous pour cette colonne pour convertir objet JSON en des colonnes séparées de dataframe:

from pandas.io.json import json_normalize 
df = json_normalize(df['A']) 
print(df) 
+0

C'est JSON pas une fois que vous avez fait votre df, c'est un dict. Mais ça me tient occupé à essayer de garder sélectivement ces colonnes à coup sûr :) – roganjosh

+0

Oui, par json je voulais dire dict uniquement. Toute idée comment puis-je supprimer toutes les lignes qui contiennent des chaînes simples comme "bonjour", "monde", etc –

+0

Veuillez répondre à cette question https://stackoverflow.com/questions/46856988/np-isreal-behavior-different-in- pandas-dataframe-and-numpy-array – Wen

Répondre

3

Je pense que je préférerais utiliser un chèque isinstance:

In [11]: df.loc[df.A.apply(lambda d: isinstance(d, dict))] 
Out[11]: 
          A 
2 {'a': 5, 'b': 6, 'c': 8} 
5 {'d': 9, 'e': 10, 'f': 11} 

Si vous voulez inclure des chiffres aussi, vous pouvez faire:

In [12]: df.loc[df.A.apply(lambda d: isinstance(d, (dict, np.number)))] 
Out[12]: 
          A 
2 {'a': 5, 'b': 6, 'c': 8} 
5 {'d': 9, 'e': 10, 'f': 11} 

Réglez ce paramètre selon les types que vous voulez inclure ...


La dernière étape, json_normalize prend un liste d'objets json, pour une raison quelconque, une série n'est pas bonne (et donne l'erreur KeyError), vous pouvez en faire une liste et votre bon à faire:

In [21]: df1 = df.loc[df.A.apply(lambda d: isinstance(d, (dict, np.number)))] 

In [22]: json_normalize(list(df1["A"])) 
Out[22]: 
    a b c d  e  f 
0 5.0 6.0 8.0 NaN NaN NaN 
1 NaN NaN NaN 9.0 10.0 11.0 
+0

Je préfère de loin cette réponse. Puisque l'autre discussion semble aller nulle part, savez-vous pourquoi 'isreal' fonctionne, alors vous pouvez me diriger dans la bonne direction en lisant? – roganjosh

+0

sur l'application "normaliser le code" après l'apposition de votre code, il donne une erreur de clé. –

+1

@roganjosh Je n'ai aucune idée, je pense que vous auriez besoin de regarder le code - je ne pense pas que np.isreal est destiné à être utilisé comme ça (je ne voudrais pas compter dessus) –

1
df[df.applymap(np.isreal).sum(1).gt(0)] 
Out[794]: 
          A 
2 {'a': 5, 'b': 6, 'c': 8} 
5 {'d': 9, 'e': 10, 'f': 11} 
+1

S'il vous plaît expliquer, ce que c'est exactement faire –

+0

Je suis également confus par ce que cela fait. Les docs n'offrent pas grand chose, certainement pour les cordes. Est-ce un effet secondaire? – roganjosh

+0

'df [df.applymap (np.isreal) .values]' peut être un peu plus concis. – cmaher

0

Si vous voulez une solution laide qui fonctionne aussi ... voici une fonction que j'ai créée qui trouve des colonnes qui contiennent seulement des chaînes, et renvoie la df moins ces lignes. (puisque votre df n'a qu'une seule colonne, vous aurez juste un dataframe contenant une colonne avec tous les dicts). Ensuite, à partir de là, vous voudrez utiliser df = json_normalize(df['A'].values) au lieu de simplement df = json_normalize(df['A']).

Pour dataframe une seule colonne ...

import pandas as pd 
import numpy as np 
from pandas.io.json import json_normalize 
def delete_strings(df): 
    nrows = df.shape[0] 
    rows_to_keep = [] 
    for row in np.arange(nrows): 
     if type(df.iloc[row,0]) == dict: 
      rows_to_keep.append(row) #add the row number to list of rows 
            #to keep if the row contains a dict 
    return df.iloc[rows_to_keep,0] #return only rows with dicts 
df = pd.DataFrame({'A': ["hello","world",{"a":5,"b":6,"c":8},"usa","india", 
         {"a":9,"b":10,"c":11}]}) 
df = delete_strings(df) 
df = json_normalize(df['A'].values) 
print(df) 
#0  {'a': 5, 'b': 6, 'c': 8} 
#1 {'a': 9, 'b': 10, 'c': 11} 

Pour une df plusieurs colonnes (fonctionne également avec une seule colonne df):

def delete_rows_of_strings(df): 
    rows = df.shape[0] #of rows in df 
    cols = df.shape[1] #of coluns in df 
    rows_to_keep = [] #list to track rows to keep 
    for row in np.arange(rows): #for every row in the dataframe 
     #num_string will count the number of strings in the row 
     num_string = 0 
     for col in np.arange(cols): #for each column in the row... 
      #if the value is a string, add one to num_string 
      if type(df.iloc[row,col]) == str: 
       num_string += 1 
     #if num_string, the number of strings in the column, 
     #isn't equal to the number of columns in the row... 
     if num_string != cols: #...add that row number to the list of rows to keep 
      rows_to_keep.append(row) 
    #return the df with rows containing at least one non string 
    return(df.iloc[rows_to_keep,:]) 


df = pd.DataFrame({'A': ["hello","world",{"a":5,"b":6,"c":8},"usa","india"], 
         'B' : ['hi',{"a":5,"b":6,"c":8},'sup','america','china']}) 
#       A       B 
#0      hello      hi 
#1      world {'a': 5, 'b': 6, 'c': 8} 
#2 {'a': 5, 'b': 6, 'c': 8}      sup 
print(delete_rows_of_strings(df)) 
#       A       B 
#1      world {'a': 5, 'b': 6, 'c': 8} 
#2 {'a': 5, 'b': 6, 'c': 8}      sup