Topic: Unione CSV  (Letto 169 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline Sendo

  • python unicellularis
  • *
  • Post: 3
  • Punti reputazione: 0
    • Mostra profilo
Unione CSV
« il: Marzo 24, 2020, 17:59 »
Salve, sono un programmatore python super-junior, ho un problema con un piccolo script:
Devo unire due o più file CSV presenti in una cartella in un unico file (e questo sono riuscito a farlo), dopo averli uniti devo avere la possibilità di inserire uno o più nomi delle colonne in input in modo che mi faccia visualizzare solo quelle colonne.
PS: alcuni nomi delle colonne sono separati da "*" in modo da visualizzare più colonne con lo stesso nome.
Grazie mille
----------------------------------------------------
import pandas as pd
import glob

all_filenames = [i for i in glob.glob("path/*.csv")]
#inserisco il path per arrivare alla cartella che contiene i file da unire

dest_file = "path/Unione_files.csv"
#inserisco il path per arrivare alla cartella dell'unione

content = []
for file_name in sorted(all_filenames):
    with open(file_name, "r") as f:
        lines = f.readlines()
    for i, l in enumerate(lines):
        if len(content) == 0:
            content.append(l)
        elif i != 0:
            content.append(l)


with open(dest_file, "w") as f:
    f.writelines(content)

while True:
    seleziona = input("Inserisci le colonne che vuoi visualizzare: ")
    if seleziona == "stop":
        break

with open(dest_file, "r") as f:
    titoli = f.readline().split(",")
    indici = []
    for titolo in titoli:
        titolo_split = titolo.split('*')
        if seleziona in titolo_split:
            indice = titoli.index(titolo)
            indici.append(indice)
    righe = f.readlines()
    lista = []
    for riga in righe:
        rigasplit = riga.split(",")
        for i in indici:
            lista.append(rigasplit)

Offline caronte

  • python erectus
  • ***
  • Post: 233
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #1 il: Marzo 24, 2020, 21:33 »
ok,  quindi pandas che l'hai importato  a fare?

Offline Sendo

  • python unicellularis
  • *
  • Post: 3
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #2 il: Marzo 25, 2020, 09:25 »
a niente infatti in quello originale l'avevo commentato, mi sono sbagliato

Offline nuzzopippo

  • python erectus
  • ***
  • Post: 228
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #3 il: Marzo 25, 2020, 10:13 »
a niente infatti in quello originale l'avevo commentato, mi sono sbagliato

Salve @Sendo, ben venuto, dato che non utilizzi pandas posso osare ad intervenire :)

Non ho compreso se il Tuo post è motivato da un problema o altro, se si, quale è il problema?, come sono strutturati i csv?, un esempio per ragionarci su è disponibile?

Per altro, nella libreria base dovrebbe essere disponibile il modulo csv
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.
>>> import csv
>>> help('csv')
Help on module csv:

NAME
    csv - CSV parsing and writing.

MODULE REFERENCE
    https://docs.python.org/3.6/library/csv
    ...

magari potrebbe esserTi utile.

Offline caronte

  • python erectus
  • ***
  • Post: 233
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #4 il: Marzo 25, 2020, 12:46 »
a niente infatti in quello originale l'avevo commentato, mi sono sbagliato

male...

Offline Sendo

  • python unicellularis
  • *
  • Post: 3
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #5 il: Marzo 25, 2020, 12:47 »
a niente infatti in quello originale l'avevo commentato, mi sono sbagliato

Salve @Sendo, ben venuto, dato che non utilizzi pandas posso osare ad intervenire :)

Non ho compreso se il Tuo post è motivato da un problema o altro, se si, quale è il problema?, come sono strutturati i csv?, un esempio per ragionarci su è disponibile?

Per altro, nella libreria base dovrebbe essere disponibile il modulo csv
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.
>>> import csv
>>> help('csv')
Help on module csv:

NAME
    csv - CSV parsing and writing.

MODULE REFERENCE
    https://docs.python.org/3.6/library/csv
    ...

magari potrebbe esserTi utile.
ciao @nuzzopippo grazie della risposta, cerco di spiegarmi meglio possibile. Sotto ti scrivo un esempio di due file.csv, se provi ad avviare lo script (inserendo i path giusti ovviamente e creando due file come prova), prende tutti i file.csv presenti in una cartella e li unisce in un unico file chiamato "Unione_files.csv", se ci sono nomi delle colonne uguali semplicemente aggiunge delle righe sotto (praticamente un append), questa cosa sono riuscoto a farla, il problema è che non voglio visualizzare tutte le colonne, devo avere la possibilità di inserire in input SOLO i nomi delle colonne che voglio visualizzare splittate per "*", prendo come esempio i file che ti ho scritto sotto, se io scrivo "Inclinazione_01", mi crea il file unito SOLO con la colonna che contiene "Inclinazione_01", quando ho finito di inserire i nomi delle colonne che voglio vedere scrivo "stop" e mi unisce i file.

|file1.csv|
-----------------------------------------------------------------
| Timestamp | Stanza_01*Inclinazione_01 | Stanza_02*Inclinazione_01
| 1         | 0.2                       | 0.432
| 2         | 0.4                       | 0.548

|file2.csv|
-----------------------------------------------------------------
| Timestamp | Stanza_01*Inclinazione_01 | Stanza_03*Inclinazione_02
| 3         | 0.3                       | 0.543
| 4         | 0.6                       | 0.972

Questo è quello che genero io:

|Unione_files.csv|
---------------------------------------------------------------------------------------------
| Timestamp | Stanza_01*Inclinazione_01 | Stanza_02*Inclinazione_01 |  Stanza_03*Inclinazione_02
| 1         | 0.2                       | 0.432                     |//
| 2         | 0.4                       | 0.548                     |//
| 3         | 0.3                       |//                         | 0.543
| 4         | 0.6                       |//                         | 0.972


Quello che vorrei dopo che ho inserito in input i nomi delle colonne, per esempio "Inclinazione_01":

|Unione_files.csv|
---------------------------------------------------
| Stanza_01*Inclinazione_01 | Stanza_02*Inclinazione_01
| 0.2                       | 0.432
| 0.4                       | 0.548
| 0.3                       | //
| 0.6                       | //

Se hai problemi puoi anche scrivermi in privato
Grazie

Offline caronte

  • python erectus
  • ***
  • Post: 233
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #6 il: Marzo 25, 2020, 15:02 »
ciao
allora anche se quella cosa che hai scritto ti funziona, in Python e' inefficiente, soprattutto se hai a che fare con grandi dimensioni;
attrezzi come pandas e similia nascono proprio per gestire tutto questo in maniera semplice, veloce, efficiente e di facile lettura:
quindi ti consiglio di guardarti la documentazione, almeno per quel che ti interessa;
dopo che ti carichi i files, la questione viene risolta con una chiamata

# loading csv...
df0 = pd.read_csv('path file to load..')
...
dfn = pd.read_csv('path file to load..')

# questa..(poi chiaramente ci sono molte altre opzioni per gestire e manipolare dataframes, con join, merge, etc...)
df_all = pd.concat([df0, ..., dfn], axis=0, ignore_index=True)

# vuoi estrapolare una o piu colonne?
df_all[['Colname1', 'Colname3', ...]]

# save file
df_all.to_csv('path to save...')

Offline nuzzopippo

  • python erectus
  • ***
  • Post: 228
  • Punti reputazione: 0
    • Mostra profilo
Re:Unione CSV
« Risposta #7 il: Marzo 25, 2020, 19:54 »
...
Se hai problemi puoi anche scrivermi in privato

No, non ho problemi ho chiesto solo per capire se vi era una domanda, in ogni caso questo è un forum, si scrive qui per condividere ed acquisire le conoscenze/esperienze messe in comune.

Fermo restando che suggerisco di far tesoro delle indicazioni di @caronte

Guardando la Tua presentazione vedo che hai iniziato da poco, non credo di essere molto più avanti di Te ma guardando il Tuo codice ho notato alcune cose, mi permetto alcuni suggerimenti, sperando di non toppare.

1° - vedo che hai inserito manualmente il path (relativo) alla directory contenente i files, potresti inserirlo quale parametro al Tuo script e leggerlo da argv di sys, per poi utilizzarlo, in un modo del genere :
import glob
import sys
import os

path = sys.argv[1]

all_filenames = [i for i in glob.glob(os.path.join(path, "*.csv"))]
#inserisco il path per arrivare alla cartella che contiene i file da unire

dest_file = os.path.join(path, "Unione_files.csv")


Poi, lo script, ovviamente, Ti da output errato nella lettura delle righe dati del secondo csv, il problema che Vuoi risolvere (se ho capito bene), ferma restando il migliore, seppur generico, approccio a Te soppoposto nel post su, ragionando a livello basico, NON puoi limitarti a copiarti le righe ma devi effettuare un minimo di valutazione dei dati.
... i dati, d'altronde, li splitti tramite la virgola ma li hai rappresentati con '|' quale separatore.

Dal Tuo script vedo che, potenzialmente, potresti voler cercare più "colonne" (lo 'stop') ... mi son divertito a buttare giù una mia versione per mero e sbrigativo esempio "terra-terra" utilizzando i comandi di base ed un minimo di list-comprehensins, che Ti ho visto usare con glob, posta una struttura directory con tre file csv, 'ci fatti :
NzP:~$ tree
.
├── data
│   ├── file1.csv
│   ├── file2.csv
│   └── file3.csv
├── nuzzo.py
├── per_post
└── sendo.py

1 directory, 6 files
NzP:~$ cat data/file1.csv
Timestamp|Stanza_01*Inclinazione_01|Stanza_02*Inclinazione_01
1|0.2|0.432
2|0.4|0.548
NzP:~$ cat data/file2.csv
Timestamp|Stanza_01*Inclinazione_01|Stanza_03*Inclinazione_02
3|0.3|0.543
4|0.6|0.972
NzP:~$ cat data/file3.csv
Timestamp|Stanza_01*Inclinazione_02|Stanza_03*Inclinazione_01
5|0.3|0.243
6|0.6|0.772
NzP:~$

Ignorando completamente l'efficienza, ho fatto uno script che si limita a stampare le colonne corrispondenti ad una ricerca ed i dati correlati
NzP:~$ python3 nuzzo.py data
Colonne da visualizzare : Inclinazione_01
Colonne da visualizzare : stop
Stanza_01*Inclinazione_01|Stanza_02*Inclinazione_01|Stanza_03*Inclinazione_01
0.2|0.432|
0.4|0.548|
0.3||
0.6||
||0.243
||0.772
NzP:~$


Lo script è questo, vedi se può esserTi utile
# -*- coding: utf-8 -*-

import glob
import sys
import os

path = sys.argv[1]

def append_data(selectors, collector, identifier, data):
    '''
    Aggiunge al dizionario di lettura i campi interessanti contenuti in
    un file csv
    '''
    headers = data[0].split('|')
    match_indexes = []
    for i in range(len(headers)):
        for sel in selectors:
            if sel in headers[i]:
                match_indexes.append(i)
    # controlla se è stato trovato qualcosa
    if not match_indexes:
        return
    founds = {}
    # crea una chiave con le intestazioni di colonna trovate
    founds['headers'] = [headers[x] for x in match_indexes]
    # crea una lista dei dati interessanti la ricerca
    match_data = []
    for data_row in data[1:]:
        match_row = [data_row.split('|')[x] for x in match_indexes]
        match_data.append(match_row)
    # crea una chiave per i dati corrispondenti
    founds['data'] = match_data
    # appende il dizionario al collector
    collector[identifier] = founds

def define_new_data(collector):
    '''
    Visualizza o registra il risultato finale
    '''
    # prepara un elenco ordinato delle intestazioni trovate
    indexed_headers = []
    for key in sorted(collector.keys()):
        for elem in sorted(collector[key]['headers']):
            if not elem in indexed_headers:
                indexed_headers.append(elem)
    indexed_headers = sorted(indexed_headers)
    # ora cicla definendo le righe dati in modo che corrispondano
    # all'elenco ordinato delle intestazioni
    data = []
    for key in sorted(collector.keys()):
        # definisce una matrice degli indici di appartenenza e destinazione
        index_matrix = []
        for i in range(len(collector[key]['headers'])):
            h = collector[key]['headers'][i]
            for j in range(len(indexed_headers)):
                if h == indexed_headers[j]:
                    index_matrix.append(j)
                    break
        # definisce la matrice dati da visualizzare
        for data_row in collector[key]['data']:
            base_row = [''] * len(indexed_headers)
            for i in range(len(data_row)):
                base_row[index_matrix[i]] = data_row[i]
            data.append(base_row)
    text = '|'.join(indexed_headers)
    for elem in data:
        text += '\n' + '|'.join(elem)
    return text

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('Utilizzo : python nuzzo.py "path_da_aprire"')
        exit(0)
    path = sys.argv[1]
    try:
        filenames = glob.glob(os.path.join(path, "*.csv"))
    except OSError as e:
        print('Errore in lettura directory', e)
        exit(1)
    if not filenames:
        print('Nessun file *.csv trovato')
        exit(0)

    search_columns = []
    selected = ''
    while selected != 'stop':
        selected = input('Colonne da visualizzare : ')
        if selected and selected != 'stop':
            search_columns.append(selected)
    if not search_columns:
        print('Nessuna ricerca impostata')
        exit(0)
    collector = {}
    for file in filenames:
        data = None
        try:
            with open(file, 'r') as f:
                data = f.read().splitlines()
        except IOError as e:
            print('Errore lettura', file, e)
        if data:
            append_data(search_columns, collector, file, data)
    print(define_new_data(collector))

Può cercare su più "elementi" ma, ovviamente, una piccola differenza comporta una aggiunta nella riga di intestazione.

Ciao