Szybko obliczyć sumę kolumny liczb

15

Zapisuję tabelę przecen, która wygląda następująco:

| 13/05/15 | 09:30-16:00 |  6.5 |
| 14/05/15 | 10:00-16:30 |  6.5 |
| 16/05/15 | 15:30-01:00 |  9.5 |
| 21/05/15 | 09:00-16:30 |  7.5 |
| 22/05/15 | 08:30-17:00 |  8.5 |
| 28/05/15 | 09:30-15:30 |  6   |
| 02/06/15 | 09:00-20:00 | 11   |
| 03/06/15 | 08:30-22:30 | 14   |

Szukam sposobu, aby szybko obliczyć sumę trzeciej kolumny i wstawić ją do bufora. Rozwiązanie, które mam na myśli, wykorzystałoby tryb blokowania wizualnego (aby wybrać wszystkie liczby) i być może rejestr wyrażeń (aby wykonać matematykę).

Czy byłoby to możliwe przy użyciu natywnych poleceń Vima? Jeśli nie, czy istnieje wtyczka, która może mi pomóc?

zool
źródło
1
Możesz zajrzeć do tego artykułu: vim.wikia.com/wiki/Using_vim_as_calculator
nobe4

Odpowiedzi:

15

Napisałem wtyczkę: https://github.com/sk1418/HowMuch, która obsługuje selekcję wizualną i wykonuje obliczenia matematyczne.

Domyślnie wtyczka obsługuje trzy mechanizmy oceny wyrażeń matematycznych: Gnu bc, python i vimscript. Możesz wykonać obliczenia dla określonego lub pozwolić, aby wtyczka automatycznie wybrała jedno dla Ciebie.

Działa z twoim przykładem w następujący sposób:

wprowadź opis zdjęcia tutaj

Aby uzyskać szczegółowe informacje, przeczytaj README na github.

Kent
źródło
Byłoby pomocne, gdybyś uwzględnił naciśnięcia klawiszy wymagane do wybrania, zsumowania i wstawienia w odpowiedzi.
pdoherty926,
@ pdoherty926 For details please read the README on github.Nawet jeśli wprowadzę tutaj naciśnięcia klawiszy dla tego problemu, nie widzę, jak pomocny może być, to tylko 3 lub 4 kombinacje klawiszy. Jeśli ktoś naprawdę potrzebuje mojego skryptu, i tak sprawdzi szczegóły.
Kent
12

Jeśli nie chcesz używać wtyczek ani upuszczać skrypt bash, możesz wykonać następujące czynności:

  • c-V {motions} "ay skopiuj kolumnę do "a
  • :let @a = substitute(@a, 'c-V c-J', '+', 'g') zamień kolumnę nowe wiersze na +
  • ic-R=c-Rauruchom zastąpiony "aprzez rejestr wyrażeń

Alternatywnie: spraw, aby wpis historii wyrażeń był ponownie użyteczny dla dalszych sum kolumn

  • ctrl-V {motions} y wstaw kolumnę do rejestru yank ""
  • ictrl-R=eval(substitute(@", '\n', '+', 'g'))

Powtarzanie dla innej kolumny:

  • ctrl-V {motion} y (bez zmian)
  • ictrl-R=<CR>lub jeśli zrobiłeś coś innego z rejestrem wyrażeń, przeglądaj historię za pomocą klawisza strzałki w górę (lub z, ctrl-Pjeśli go odwzorowałeś):
    ictrl-R=<up>...<up><CR>
Hovercouch
źródło
1
Z jakiegoś powodu udało mi się użyć twojego rozwiązania tylko z podwójnymi cudzysłowami "zamiast pojedynczych cudzysłowów 'w substitutepoleceniu. Czy wiesz, czy jest tego jakiś powód?
vappolinario,
@vappolinario działa dla mnie w obie strony, więc obawiam się, że nie wiem, przepraszam.
Hovercouch,
@Hovercouch Czy mógłbyś rozwinąć trzeci krok? Jak dokładnie przejść do wymiany przez rejestr wyrażeń?
pdoherty926
Co powiesz na utworzenie mapy: `nnoremap <cs>: s / $ / \ = eval (substitute (@ 0, '[^ 0-9]', '+', 'g')) / <cr>`
SergioAraujo
9
:r!awk '{sum+=$6} END {print "Total: "sum}' %

Wyjaśnienie:

:r ........... read (put result in this file)
! ............ external command
awk .......... external tool
{sum+=$6} .... sixth field (awk considers spaces as field separator)
END .......... at the end
{print "Total: "sum} --> string "Total: " plus your result
% ............ current file

Próbowałem funkcji, która działa tutaj:

" This function requires you select the numbers
fun! SumVis()
    try
        let l:a_save = @a
        norm! gv"ay
        let @a = substitute(@a,'[^0-9. ]','+','g')
        exec "norm! '>o"
        exec "norm! iTotal \<c-r>=\<c-r>a\<cr>"
     finally
        let @a = l:a_save
     endtry
endfun
vnoremap <leader>s :<C-u>call SumVis()<cr>

Korzystając z powyższej mapy, wszystko co musisz zrobić po załadowaniu funkcji, to wybrać liczby, które chcesz zsumować i użyć, <leader>saby zsumować wybrany obszar.

Objaśnienie funkcji:

Używa try/finally/endtryekstrakcji do wychwytywania błędów.

let l:a_save = @a .......... if whe have register 'a' we save it temporarelly
norm! gv"a  ................................... gv --> reselects and captures selection to 'register a'
let @a = substitute(@a,'[^0-9. ]','+','g') .... removes all but numbers, dots and spaces from 'register a' and puts '+' among the numbers
exec "norm! '>o"  ............................. opens new line bellow selection. see :h '>
exec "norm! iTotal: \<c-r>=\<c-r>a\<cr>" ...... insert "Total: " plus 'expression register result
let @a = l:a_save ............................. restores original 'a' register content

Jeśli chcesz wypróbować tę funkcję, wykonaj następujące czynności: Skopiuj tę funkcję w przeglądarce i uruchom to polecenie na vimie, :@+ co pozwoli ci :call SumVis()normalnie korzystać .

:@+ ......... loads `+` register making the function avaiable

Musi dokonać wizualnego wyboru bloku za pomocą ctrl+ v, odznaczyć i wreszcie wywołać funkcję. Lub możesz użyć sugerowanej mapy, która sama usuwa zaznaczenie przed obliczeniem.

SergioAraujo
źródło
7

Moja wtyczka csv na to pozwala. Użyj :SumColpolecenia i koniecznie przeczytaj dokumentację.

Christian Brabandt
źródło
5

Tworzenie wtyczki lub kodowanie tego w vimscriptie wydaje się trochę ciężkie. Wierzę w vim bez wtyczek i dobrą kompozycję z zewnętrznymi narzędziami.

Oto jednorazowe polecenie oparte na użytkownikach 2571881, które działa, nawet jeśli bufor nie został zapisany.

:%!awk -F '|' '{print; sum+=$4}; END {print "Total: "sum}'

Jeśli chcesz zapisać to polecenie do przyszłego użytku, możesz je nazwać:

:command! -range=% -nargs=1 SumColumn <line1>,<line2>!awk -F '|' '{print; sum+=$('<args>' + 1)} END {print "Total: "sum}'

Działa z selekcją wizualną. Jeśli wybierzesz kilka wierszy i przejdziesz do trybu komend, vim poprzedzi twoją komendę :'<,'>, która jest zakresem linii dla selekcji wizualnej. Możesz więc uruchomić:

:'<,'>SumColumn 3

i będzie sumować tylko trzecią kolumnę wybranych wierszy. Domyślnie zakres jest %taki

:SumColumn 3

zsumuje trzecią kolumnę wszystkich wierszy.

EDYCJA: Jeśli chcesz móc określić inne separatory pól i domyślnie kolumnę zliczoną do ostatniej, możesz przykryć polecenie bashi obsłużyć nim argumenty, w następujący sposób:

:command! -range=% -nargs=* SumColumn <line1>,<line2>!bash -c 'awk -F ${2:-|} "{print; sum+=\$(${1:-NF - 2} + 1)} END {print \"Total: \"sum}"' sumcolumn <args>

Teraz,

:SumColumn

policzy ostatnią kolumnę tabeli za pomocą „|” separatory pól,

:SumColumn 3

policzy trzecią kolumnę tabeli za pomocą „|” separatory pól oraz

:SumColumn 3 +

policzy trzecią kolumnę tabeli z separatorami pól „+”.

JoL
źródło
Jak radzić sobie z innymi możliwymi separatorami pól? Tylko po to, aby rozwiązanie było bardziej ogólne.
SergioAraujo
@ user2571881, zredagowałem odpowiedź, pokazując to.
JoL
@JoL dodając funkcje takie jak SumColumnvimrc oznacza, że ​​masz po prostu swoje „pluginy” w swoim vimrc. Mamy nadzieję, że jesteś dobry w utrzymywaniu tego z czasem. Dla mnie wtyczki zapewniają dokumentację, podział na znaczące części, korzystając z pomysłowości innych. Przyczyniam się do upstreamu, który ulepsza niesamowite wtyczki, których nikt nie ma czasu, aby stworzyć je wszystkie (z wyjątkiem tpope). Czy nie używasz vim-surround, vim-fugitive, vim-easy-align / vim-lion, vim-unimpaired, vim-commentary, ultisnips lub specyficznych dla ft, takich jak vim-go, vim-rails, vimtex?
Hotschke
@Hotschke Kiedy tu dotarłem, zobaczyłem pytanie i pomyślałem: „cóż, po prostu przejedź przez awk”. Ale potem zobaczyłem, że zaakceptowaną odpowiedzią było: „hej, pobierz te setki wtyczek LOC i zainstaluj je”. Trzecia odpowiedź brzmiała: „hej, pobierz tysiące wtyczek LOC i zainstaluj je”. To przesada i wzdęcia. Nawet jeśli trzeba sumować kolumny więcej niż raz w życiu, to przesada. Moja odpowiedź ma na celu pokazać, jak możesz to zrobić w pojedynczym poleceniu bez wtyczek, bez bzdur, jeśli musisz to zrobić tylko raz, oraz w jaki sposób możesz wykonać proste polecenie z parametrami, jeśli musisz to zrobić często.
JoL
@Hotschke Aby odpowiedzieć na twoje pytanie, instalowałem każdą wtyczkę pod słońcem, która wyglądała zdalnie fajnie, ale potem mój vim był niesamowicie wolny (czytaj „trochę leniwy”, co jest nie do zniesienia dla edytora). Przyglądając się bliżej dokumentom vim, zdałem sobie sprawę, że tak naprawdę nie potrzebowałem wtyczek. Wiele podstawowych cech było wystarczająco dobrych, a dla tych, których vim nie miał, powłoka była właściwą drogą. Zasadniczo (ignorując wyjątki, które się z tym wiąże), zgodnie z filozofią Uniksa, vim jest edytorem, który dobrze współpracuje z innymi narzędziami systemu operacyjnego. Uważam, że to sposób na najlepsze wykorzystanie tego. Brak wtyczek od tego czasu.
JoL
2

Jeśli kolumny są odpowiednio wyrównane, można to zrobić za pomocą prostego narzędzia oneliner.

  1. najpierw wybierz kolumnę w trybie wizualnym blokowym, ponieważ inne odpowiedzi wykazały -> CTRL-V+ przesuń kursor
  2. wybierz za pomocą y
  3. type: :echo eval(join(split(@", '\_s\+'), '+'))który dzieli szarpany tekst na spacje i nowe wiersze, ponownie łączy element ze +znakiem i ocenia ciąg.
  4. inny sposób postępowania: zamień znaki nowej linii na +i oceń: :echo eval(substitute(@", "\n", '+', 'g'))- eval()to najbliższa rzecz, reducejaką mamy.

Jeśli nie, będziesz musiał użyć innych sztuczek, aby policzyć pola. Na przykład split(getline('.'), "[ \t|]\\+")można użyć do podzielenia kolumn z wiersza w tablicy. Odtąd staje się to tak proste, jak:

  1. wybierz swoje linie w trybie wizualnym
  2. :echo eval(join(map(getline("'<", "'>"), { -> split(v:val, "[ \t|]\\+")[2] }), '+'))

Aby pozbyć się magicznych wartości (pole numer - 1, i +), może stać się poleceniem

:command! -range=% -nargs=+ OnField 
    \ echo { field, what -> eval(join(map(getline(<line1>, <line2>), { -> split(v:val, "[ \t|]\\+")[field-1] }), what))}(<f-args>)

Które mogą być używane z:

:OnField  3 +
:2,5OnField  3 +
:'<,'>Onfield 3 *   " after line-wise selection
....

Uwaga: tutaj używam lambd z Vim 7.4.1xxx

Luc Hermitte
źródło
1

vmap ++z wtyczki vmathDamiana Conwaya

  1. Zainstaluj wtyczkę z github (tylko 178 sloc) np

    $ wget https://raw.githubusercontent.com/thoughtstream/Damian-Conway-s-Vim-Setup/master/plugin/vmath.vim -P ~/.vim/pack/manual/start/damians-tools/plugin
    
  2. Dodaj mapowanie do swojego vimrc

    vmap <silent><expr>  ++  VMATH_YankAndAnalyse()
    

    Sugerowałbym jednak użyć czegoś innego, np gA

  3. Przejdź do trzeciej 2f|kolumny i wybierz kolumnę w trybie wizualnego blokowania<C-V>G$
  4. Naciśnij ++(lub wybrane mapowanie)
  5. Wyniki są wyświetlane i przechowywane w rejestrach (suma w s)
  6. Wstaw sumę z rejestru s, np. Za pomocą"sp

Prezentację tej wtyczki można znaleźć w filmie na YouTube Damian Conway „More Natychmiast Better Vim” - OSCON 2013 (od 29 minuty).

Hotschke
źródło
1

Zewnętrzne narzędzie cli csvstatz csvkit

:!csvstat -d '|' -H -c 4 --sum %
69.5

Krótkie wyjaśnienie opcji

  • -d DELIMITERZnak ograniczający wejściowy plik CSV. Tutaj |.
  • -H Określ, że wejściowy plik CSV nie ma wiersza nagłówka.
  • -c COLUMNSRozdzielona przecinkami lista indeksów lub nazw kolumn do zbadania. Domyślnie wszystkie kolumny.
  • --sum Tylko sumy wyjściowe.

To narzędzie zapewnia także min., Maks., Średnią, medianę, odchylenie standardowe (odchylenie standardowe), zliczanie unikalnych wartości, listę częstych wartości.

Wstaw do pliku za pomocą

<C-r>=system("csvstat -d '|' -H -c 4 --sum FILENAME 2> /dev/null")  

Instalacja

Na macOS csvkit jest dostępny przez homebrew oraz na Debian / Ubuntu i podobny, z którym można go zainstalować $ sudo apt install csvkit.

Hotschke
źródło