J'ai un grand ensemble de données avec de nombreuses colonnes au format JSON (compressé). J'essaie de le convertir en parquet pour un traitement ultérieur. Certaines colonnes ont une structure imbriquée. Pour l'instant, je veux ignorer cette structure et simplement écrire ces colonnes sous forme de chaîne (JSON).
Donc, pour les colonnes que j'ai identifiées, je fais:
df[column] = df[column].astype(str)
Cependant, je ne sais pas quelles colonnes sont imbriquées et lesquelles ne le sont pas. Quand j'écris avec du parquet, je vois ce message:
<stack trace redacted>
File "pyarrow/_parquet.pyx", line 1375, in pyarrow._parquet.ParquetWriter.write_table
File "pyarrow/error.pxi", line 78, in pyarrow.lib.check_status
pyarrow.lib.ArrowInvalid: Nested column branch had multiple children: struct<coordinates: list<item: double>, type: string>
Cela signale que je n'ai pas réussi à convertir l'une de mes colonnes d'un objet imbriqué en chaîne. Mais quelle colonne est à blâmer? Comment le découvrir?
Lorsque j'imprime le .dtypes
de mon pandas dataframe, je ne peux pas faire la différence entre la chaîne et les valeurs imbriquées car les deux apparaissent sous la forme object
.
EDIT: l'erreur donne un indice sur la colonne imbriquée en affichant les détails de la structure, mais cela prend du temps à déboguer. De plus, il n'imprime que la première erreur et si vous avez plusieurs colonnes imbriquées, cela peut devenir assez ennuyeux
J'ai eu un problème simialir en travaillant avec Pyspark et un jeu de données en streaming, certaines colonnes étaient imbriquées et d'autres pas.
Étant donné que votre dataframe peut ressembler à ceci:
df = pd.DataFrame({'A' : [{1 : [1,5], 2 : [15,25], 3 : ['A','B']}],
'B' : [[[15,25,61],[44,22,87],['A','B',44]]],
'C' : [((15,25,87),(22,91))],
'D' : 15,
'E' : 'A'
})
print(df)
A \
0 {1: [1, 5], 2: [15, 25], 3: ['A', 'B']}
B C D E
0 [[15, 25, 61], [44, 22, 87], [A, B, 44]] ((15, 25, 87), (22, 91)) 15 A
Nous pouvons empiler votre dataframe et utiliser apply
avec type
pour obtenir le type de chaque colonne et le transmettre à un dictionnaire.
df.head(1).stack().apply(type).reset_index(0,drop=True).to_dict()
out:
{'A': dict, 'B': list, 'C': Tuple, 'D': int, 'E': str}
avec cela, nous pouvons utiliser une fonction pour renvoyer un Tuple des colonnes imbriquées et non imbriquées.
def find_types(dataframe):
col_dict = dataframe.head(1).stack().apply(type).reset_index(0,drop=True).to_dict()
unnested_columns = [k for (k,v) in col_dict.items() if v not in (dict,set,list,Tuple)]
nested_columns = list(set(col_dict.keys()) - set(unnested_columns))
return nested_columns,unnested_columns
nested,unested = find_types(df)
df[unested]
D E
0 15 A
print(df[nested])
C A \
0 ((15, 25, 87), (22, 91)) {1: [1, 5], 2: [15, 25], 3: ['A', 'B']}
B
0 [[15, 25, 61], [44, 22, 87], [A, B, 44]]
Si vous voulez juste savoir quelle (s) colonne (s) est (sont) le (s) coupable (s), alors écrivez simplement une boucle qui écrit une colonne à la fois et stocke celles qui échouent ...
bad_cols = []
for i in range(df.shape[1]):
try:
df.iloc[:, [i]].to_parquet(...)
except KeyboardInterrupt:
raise
except Exception: # you may want to catch ArrowInvalid exceptions instead
bad_cols.append(i)
print(bad_cols)
En utilisant une fonction utilitaire générale comme infer_dtype()
in pandas, vous pouvez déterminer si la colonne est imbriquée ou non.
from pandas.api.types import infer_dtype
for col in df.columns:
if infer_dtype(df[col]) == 'mixed' :
# ‘mixed’ is the catchall for anything that is not otherwise specialized
df[col] = df[col].astype('str')
Si vous ciblez des types de données spécifiques, consultez Dtype Introspection