opendatasicilia / tansignari

"T'ansignari e t'appeddiri"
http://tansignari.opendatasicilia.it
Creative Commons Attribution 4.0 International
18 stars 10 forks source link

ordinare numericamente un array alfanumerico #196

Closed pigreco closed 1 year ago

pigreco commented 2 years ago

Ho una colonna di un CSV caratterizzata da una serie di numeri separati da - come in figura:

image

come ordinare numericamente (Natural Sorting) i valori all'interno delle celle nella colonna scritta?

esempio:

da: 5/A-5-16/B-4-18-8-15-22-3-14-12-6-16/A-9-11-13-7-21-1-19-10-7/A-17-20-2

a: 1-2-3-4-5-5/A-6-7-7/A-8-9-10-11-12-13-14-15-16/A-16/B-17-18-19-20-21-21

usare la funzione array_sort ordinerebbe alfabeticamente e non va.

allego un csv di prova ordinare_scritta.csv

aborruso commented 2 years ago

Caro totò, con Miller 5 (dovresti avere questo), un modo non elegante è questo:

mlr --csv nest --explode --values --across-records -f scritta --nested-fs "-" then \
put -S '$toto=regextract($scritta,"[0-9]+")' then \
sort -f den_estesa -n toto then \
cut -x -f toto then \
nest --implode --values --across-records --nested-fs "-" -f scritta ordinare_scritta.csv

Di base mette tutto in colonna splittando per -, poi crea una colonna con i soli numeri, ordina per questa e poi rimetta tutto insieme

aborruso commented 2 years ago

Una versione, che ordina anche 12B, 12, 12A, per lo stesso record

mlr --csv nest --explode --values --across-records -f scritta --nested-fs "-" then \
put -S '$totoN=regextract($scritta,"[0-9]+");$totoT=regextract($scritta,"[a-zA-Z]+")' then unsparsify then sort -f den_estesa -n totoN -f totoT then cut -r -x -f "toto+" then nest --implode --values --across-records --nested-fs "-" -f scritta  ordinare_scritta.csv
pigreco commented 2 years ago

Una versione, che ordina anche 12B, 12, 12A, per lo stesso record

funziona perfettamente, grazie:

image

pigreco commented 2 years ago

ricetta fatta e pubblicata,

grazie

https://tansignari.opendatasicilia.it/ricette/script/ordinare_elementi_array/

gpirrotta commented 2 years ago

Ciao Totò, anche se Andrea ti ha già risposto inserisco qui sotto due versioni della stessa ricetta in python:

La prima più pythonica...

import pandas as pd
df = pd.read_csv('ordinare_scritta.csv')
df['scritta']=df.scritta.map(lambda x: 
                             "-".join([item.replace('/0','') 
                             for item in [item[1:] if item[0]=='0' else item 
                             for item in sorted([t.zfill(2)+"/0" if t.isdigit() else t 
                             for t in [f'0{t}' if t.find('/')==1 else t 
                             for t in x.split('-')]], key=str)]]))   
df.to_csv('ordina_scritta_ok.csv',index=False)

...la stessa di prima, più leggibile però:

import pandas as pd
df = pd.read_csv('ordinare_scritta.csv')
def ordina(x):
    tmp = x.split('-')
    a = [f"0{t}" if t.find('/')==1 else t for t in tmp]
    b = [t.zfill(2)+"/0" if t.isdigit() else t for t in a]
    tmp2=sorted(b, key=str)
    first_ord = [item[1:] if item[0]=='0' else item for item in tmp2]
    second_ord =  [item.replace('/0','') for item in first_ord]
    return "-".join(second_ord)

df['scritta']=df.scritta.map(ordina)   
df.to_csv('ordina_scritta_ok.csv',index=False)

Ciao

aborruso commented 2 years ago

Quanto voglio bene a @gpirrotta ?💜

pigreco commented 2 years ago

grazie mille @gpirrotta, sono belle soluzioni, prima o poi inizierò a studiare Python sul serio.

aggiungerò le soluzioni alla ricetta ;-)

pigreco commented 2 years ago

aggiungo anche l'ultima arrivata, soluzione usando le espressioni di QGIS

array_to_string(
array_foreach(
    array_sort(
        with_variable('lista',
            string_to_array('5/A-5-4-8-3-14-6-9-7-1-10-7/B-2-7/A' ,'-'),
            array_foreach(generate_series(0, array_length(@lista)-1),
            lpad(regexp_substr( (@lista[@element]),'(\\d+)'),3,'0')
            ||'|'||
            if(regexp_substr((@lista[@element]),'([a-zA-Z/]+)') !='',
               regexp_substr((@lista[@element]),'([a-zA-Z/]+)'),
               ' ') -- uno spazio
            ||'|'||
            @lista[@element]))),
regexp_replace( @element,'^.+\\|(.+)$','\\1'))
)

image

Korto19 commented 2 years ago

Suggerirei questa forma per una funzione personalizzata che restituisca un array:

image commentando l'ultima riga e decommentando la penultima restituisce una stringa

from qgis.core import *
from qgis.gui import *

@qgsfunction(args='auto', group='Custom', referenced_columns=[])
def array_sort_special(value1, feature, parent):
    """
    Ordina un array alfanumerico numericamente
    <h2>Example usage:</h2>
    <ul>
      <li>array_sort_special('5/A-5-4-8-3-6-9-7-1-10-7/B-2-7/A') -> ['1','2','3','4','5','5/A','6','7','7/A','7/B','8','9','10']</li>
    </ul>
    """

    data = value1.split('-')
    r = sorted(data, key=lambda item: (int(item.partition('/')[0])
    if item[0].isdigit() else float('inf'), item))
    #return (','.join(r))
    return r
pigreco commented 2 years ago

Suggerirei questa forma per una funzione personalizzata che restituisca un array:

grazie @Korto19 , anche questa soluzione andrà nella ricetta :-)

aborruso commented 2 years ago

Ecco una soluzione ancora più light, che sfrutta il natural sorting e le utility di sistema.

Nella shell infatti basta usare

echo "15,1,2/AX,22,1/C,1/A,1/BA,2,3" | tr , "\n" | sort -V | paste -sd, -

per avere

1,1/A,1/BA,1/C,2,2/AX,3,15,22

E questo è portabile penso in tutti gli ambienti. In Miller c'è la funzione system. Ad esempio a partire da

a,b
1,"15,1,2/AX,22,1/C,1/A,1/BA,2,3"

si lancia

<input.txt mlr --c2p --barred cat  then put -S '$toto=system("echo ".$b." | tr , \"\n\" | sort -V | paste -sd, -")'

e si ottiene

+---+-------------------------------+-------------------------------+
| a | b                             | toto                          |
+---+-------------------------------+-------------------------------+
| 1 | 15,1,2/AX,22,1/C,1/A,1/BA,2,3 | 1,1/A,1/BA,1/C,2,2/AX,3,15,22 |
+---+-------------------------------+-------------------------------+
pigreco commented 1 year ago

La ricetta è stata fatta, chiudo

https://tansignari.opendatasicilia.it/ricette/script/ordinare_elementi_array/