1
votes

Élimination des sauts de ligne indésirables dans un fichier CSV

J'ai un très gros fichier CSV (million + lignes), sur lequel j'aimerais effectuer quelques opérations. Le problème est que certaines lignes ont des sauts de ligne indésirables, comme ceci:

New York City; Iron Man; no superpowers
Metropolis; Superman; superpowers;
New York City; Spider-Man; superpowers;
Gotham; Batman; no superpowers;
New York City; Doctor Strange; superpowers;

Le fichier a donc trois colonnes ( location , super-héros , superpouvoirs ). Puisque l'entrée de Spider-Man est défectueuse, car elle a des sauts de ligne entre ses entrées, les pandas supposent à tort qu'il s'agit de trois lignes séparées, avec NaNs dans la deuxième et la troisième colonne.

Mon idée était de résoudre ce problème lors de l'importation avec regex. Selon ce site Web , cette expression régulière correspond correctement aux lignes souhaitées et ne correspond pas au ceux défectueux (par exemple Spider-Man).

(. * [;]. * [;]. *)

Son inverse ( (?! (. * [;]. * [;]. *)) , cependant, ne fonctionne pas, car non seulement il ne correspond pas aux trois lignes défectueuses, mais aussi à la troisième entrée de chaque ligne normale.

Mon autre approche consistait simplement à définir le nombre de colonnes, puis à supprimer tous les sauts de ligne du fichier entier. Cela ne fonctionnait pas non plus.

superhero_df = pd.read_csv("superheroes.csv", sep=' *; *', skiprows=12, names=["location", "superhero", "superpower"], index_col=False, engine="python")
superhero_df = superhero_df.replace('\r\n','', regex=True)


1 commentaires

Vous pouvez exécuter quelque chose comme ideone.com/jTP9S3 pour prétraiter le fichier, écrivez-le dans un autre (utilisez f.write (newfile) au lieu des commandes print ), puis chargez-le comme d'habitude. Cela signifie que vous n'avez pas à lire le fichier entier en mémoire. Ce que vous essayez est de charger d'abord le contenu dans dataframe, puis d'exécuter replace , alors que ce devrait être l'inverse: d'abord remplacer, puis charger.


5 Réponses :


1
votes

Et à propos de ça:

import re

regex = r"^([^;]+);[\r\n]*([^;]+);[\r\n]*([^;]+);"

test_str = ("New York City; Iron Man; no superpowers;\n"
    "Metropolis; Superman; superpowers;\n"
    "New York City;\n"
    "Spider-Man;\n"
    "superpowers;\n"
    "Gotham; Batman; no superpowers;\n"
    "New York City; Doctor Strange; superpowers;\n\n")

subst = "\\1;\\2;\\3;"

# You can manually specify the number of replacements by changing the 4th argument
result = re.sub(regex, subst, test_str, 0, re.MULTILINE | re.DOTALL)

if result:
    print (result)

et remplacer par:

\1;\2;\3;

regex101

exécuter ici

^([^;]+);[\r\n]*([^;]+);[\r\n]*([^;]+);


1 commentaires

Malheureusement, cela ne semble pas fonctionner avec mes données, car le résultat est toujours le même. L'impression des lignes défectueuses avec des pandas génère les résultats suivants: 'location New York City \ nsuperhero NaN \ nsuperpower None \ nName: 131061, dtype: object'



1
votes

L'expression régulière suivante élimine les sauts de ligne indésirables et autres espaces blancs tous les trois champs. Cela suppose que les champs n'ont pas de point-virgule interne:

print(re.sub(r'([^;]*);\s*([^;]*);\s*([^;]*);\s+', r'\1;\2;\3\n', 
      line, flags=re.M))
#New York City; Iron Man;no superpowers
#Metropolis;Superman;superpowers
#New York City;Spider-Man;superpowers
#Gotham;Batman;no superpowers
#New York City;Doctor Strange;superpowers

Vous pouvez l'exécuter en boucle pour prétraiter le fichier avant d'utiliser Pandas.


3 commentaires

Malheureusement, cela ne semble pas fonctionner, car le résultat est toujours le même. Imprimer les sorties des lignes de défaut: 'location New York City \ nsuperhero NaN \ nsuperpower None \ nName: 131061, dtype: object'


J'ai utilisé vos exemples de données et j'ai obtenu les résultats attendus. D'où viennent les nans? Vous devez nettoyer le fichier CSV avant de le charger dans les pandas. Il ne devrait y avoir aucun nans dedans.


Tu avais raison. J'ai parcouru le fichier avant de le charger, mais il ne semble toujours pas fonctionner. C'est ce que j'ai fait: tmp = "" pour la ligne dans data.splitlines (): line = re.sub (r '([^;] *); \ s * ([^;] *); \ s * ([^;] *); \ s + ', r' \ 1; \ 2; \ 3 \ n ', ligne, drapeaux = re.M) tmp + = ligne



0
votes

Solution la plus simple:

import pandas as pd
import re

string = """New York City; Iron Man; no superpowers;
Metropolis; Superman; superpowers;
New York City;
Spider-Man;
superpowers;
Gotham; Batman; no superpowers;
New York City; Doctor Strange; superpowers;"""

cities=[]
superheros=[]
superpowers = []

splited_list = re.split(';', string)
splited_list.pop(len(splited_list) - 1 )

i = 0

while i < len(splited_list) - 1:
    cities.append(splited_list[i])
    superheros.append(splited_list[i + 1])
    superpowers.append(splited_list[i + 2])

    i = i + 3


df = pd.DataFrame({
    "City": cities,
    "Superhero": superheros,
    "superpowers": superpowers
})


0 commentaires

0
votes

Voici mon approche, non optimisée pour les performances mais je peux le faire:

[['New York City', 'Iron Man', 'no superpowers'],
 ['Metropolis', 'Superman', 'superpowers'],
 ['New York City', 'Spider-Man', 'superpowers'],
 ['Gotham', 'Batman', 'no superpowers'],
 ['New York City', 'Doctor Strange', 'superpowers']]

le résultat est une liste de listes:

from pprint import pprint

def main():
    count=0
    outer_list=[]
    row=[]
    with open('superheroes.csv') as f:
        for line in f:
            for word in line.split(";"):
                if not str.isspace(word):
                    word=word.strip()
                    row.append(str(word))
                    count = count + 1
                    if count % 3 == 0:
                        outer_list.append(row)
                        row=[]
    pprint(outer_list)

if __name__== "__main__":
    main()

p>


0 commentaires

1
votes

Si j'étais vous, je réécrirais toutes les données dans un nouveau fichier texte avec une simple itération sur le fichier texte source et chargerais le fichier résultant dans Pandas, aucun re nécessaire:

pd.read_csv('target.txt', header=None, sep=';')

#                0                1                2
# 0  New York City         Iron Man   no superpowers
# 1     Metropolis         Superman      superpowers
# 2  New York City       Spider-Man      superpowers
# 3         Gotham           Batman   no superpowers
# 4  New York City   Doctor Strange      superpowers

Résultat:

with open('source.txt') as fin, open('target.txt', 'w') as fout:
    lc = 0
    for line in fin:
        lc += line.count(';')
        if  lc < 3:
            fout.write(line.strip())
        else:
            fout.write(line.strip()[:-1] + '\n')
            lc = 0

Lecture dans Pandas:

pd.read_csv('target.txt', header=None, sep=';', usecols=range(3))

#                0                1                2
# 0  New York City         Iron Man   no superpowers
# 1     Metropolis         Superman      superpowers
# 2  New York City       Spider-Man      superpowers
# 3         Gotham           Batman   no superpowers
# 4  New York City   Doctor Strange      superpowers

Remarque: usecols n'est nécessaire qu'à cause du point-virgule de fin. Cela peut être évité en important avec

# New York City; Iron Man; no superpowers;
# Metropolis; Superman; superpowers;
# New York City;Spider-Man;superpowers;
# Gotham; Batman; no superpowers;
# New York City; Doctor Strange; superpowers;

Lecture dans Pandas:

with open('source.txt') as fin, open('target.txt', 'w') as fout:
    lc = 0
    for line in fin:
        lc += line.count(';')
        if  lc < 3:
            fout.write(line[:-1])
        else:
            fout.write(line)
            lc = 0


0 commentaires