Calcule rápidamente el total de una columna de números

15

Estoy escribiendo una tabla de rebajas que se ve así:

| 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   |

Estoy buscando una manera de calcular rápidamente el total de la tercera columna e insertarlo en el búfer. La solución que tengo en mente haría uso del modo de bloque visual (para seleccionar todos los números) y tal vez del registro de expresión (para hacer los cálculos).

¿Sería esto posible usando los comandos nativos de Vim? Si no, ¿hay algún complemento que pueda ayudarme?

zool
fuente
1
Puedes echar un vistazo a este artículo: vim.wikia.com/wiki/Using_vim_as_calculator
nobe4

Respuestas:

15

Escribí un complemento: https://github.com/sk1418/HowMuch que admite la selección visual y hace cálculos matemáticos.

Por defecto, el complemento admite tres motores de evaluación de expresiones matemáticas: Gnu bc, python y vimscript. Puede hacer los cálculos en uno determinado o dejar que el complemento elija uno automáticamente.

Funciona con tu ejemplo así:

ingrese la descripción de la imagen aquí

Para más detalles, lea el archivo README en github.

Kent
fuente
Sería útil si incluyera las teclas necesarias para seleccionar, sumar e insertar en su respuesta.
pdoherty926
@ pdoherty926 For details please read the README on github.Incluso si pongo las teclas presionadas para este problema aquí, no veo cuán útil podría ser, son solo 3 o 4 combinaciones de teclas. Si alguien realmente necesita mi guión, él / ella verificará los detalles de todos modos.
Kent
12

Si no desea usar complementos o pasar a un script bash, puede hacer algo como lo siguiente:

  • c-V {motions} "ay copiar columna en "a
  • :let @a = substitute(@a, 'c-V c-J', '+', 'g') reemplazar las nuevas líneas de columna con +
  • ic-R=c-Raejecutar el reemplazado a "através del registro de expresión

Alternativamente: haga que la entrada del historial de expresiones sea reutilizable para otras sumas de columnas

  • ctrl-V {motions} y poner columna en registro de extracción ""
  • ictrl-R=eval(substitute(@", '\n', '+', 'g'))

Repitiendo para otra columna:

  • ctrl-V {motion} y (sin alterar)
  • ictrl-R=<CR>o si hizo algo más con el registro de expresión, recorra el historial con la tecla de flecha hacia arriba (o con ctrl-Psi lo reasignó):
    ictrl-R=<up>...<up><CR>
Hovercouch
fuente
1
Por alguna razón, solo logré usar su solución con comillas dobles en "lugar de comillas simples 'en el substitutecomando. ¿Sabes si hay alguna razón para eso?
vappolinario
@vappolinario funciona en ambos sentidos para mí, así que me temo que no sé, lo siento.
Hovercouch
@Hovercouch ¿Podría dar más detalles sobre el tercer paso? ¿Cómo, exactamente, se podría ejecutar el reemplazo a través del registro de expresión?
pdoherty926
¿Qué tal hacer un mapa: `nnoremap <cs>: s / $ / \ = eval (sustituto (@ 0, '[^ 0-9]', '+', 'g')) / <cr>`
SergioAraujo
9
:r!awk '{sum+=$6} END {print "Total: "sum}' %

Explicación:

: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

He estado probando una función que funciona aquí:

" 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>

Usando el mapa anterior incluido, todo lo que tiene que hacer después de cargar la función es seleccionar los números que desea sumar y usar <leader>spara resumir el área seleccionada.

Explicación de la función:

Utiliza try/finally/endtryextructure para capturar errores.

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

Si desea probar esta función, haga lo siguiente: Copie esta función en su navegador y ejecute este comando en vim, :@+ esto le permitirá usarlo :call SumVis()normalmente.

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

Necesita que hagas una selección de bloque visual con ctrl+v , anule la selección y finalmente llame a la función. O puede usar el mapa sugerido que elimina por sí solo la selección antes de calcular.

SergioAraujo
fuente
7

Mi complemento csv lo permite. Use el :SumColcomando y asegúrese de leer la documentación.

Christian Brabandt
fuente
5

Hacer un complemento o codificar esto en vimscript parece un poco pesado. Creo en un vim sin complementos y una buena composición con herramientas externas.

Aquí hay un comando de 1 vez, basado en user2571881, que funciona incluso si el búfer no se ha guardado.

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

Si desea guardar este comando para usarlo en el futuro, puede nombrarlo:

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

Funciona con selección visual. Si selecciona algunas filas y entra en modo de comando, vim agregará un prefijo a su comando :'<,'>, que es el rango de línea para la selección visual. Para que pueda ejecutar:

:'<,'>SumColumn 3

y solo sumará la tercera columna de las filas seleccionadas. Por defecto el rango es %, entonces

:SumColumn 3

sumará la 3ra columna de todas las líneas.

EDITAR: si desea poder especificar otros separadores de campo y predeterminar la columna contada hasta el último, puede cubrir el comando bashy manejar los argumentos con él, de esta manera:

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

Ahora,

:SumColumn

contará la última columna de una tabla con "|" separadores de campo,

:SumColumn 3

contará la tercera columna de una tabla con "|" separadores de campo, y

:SumColumn 3 +

contará la tercera columna de una tabla con separadores de campo "+".

JoL
fuente
¿Cómo se puede tratar con otros posibles separadores de campo? Solo por hacer la solución más genérica.
SergioAraujo
@ user2571881, he editado la respuesta, mostrándola.
JoL
@JoL agregando funciones como SumColumnvimrc significa que simplemente tiene sus 'complementos' en su vimrc. Con suerte, eres bueno para mantener esto con el tiempo. Para mí, los complementos proporcionan documentación, separación en partes significativas, aprovechando el ingenio de otros. Contribuyo a upstream que mejora los complementos increíbles que nadie tiene tiempo para crearlos por su cuenta (excepto tpope). ¿No utilizas vim-surround, vim-fugitive, vim-easy-align / vim-lion, vim-unimpaired, vim-commentary, ultisnips o ft-específicas como vim-go, vim-rails, vimtex?
Hotschke
@Hotschke Cuando llegué aquí, vi la pregunta y pensé: "bueno, simplemente pasa por awk". Pero luego, vi que la respuesta aceptada era: "oye, descarga este complemento de cientos de LOC e instálalo". La tercera respuesta fue: "oye, descarga este complemento de miles de LOC e instálalo". Es exagerado e hinchado. Incluso si necesita sumar columnas más de una vez en su vida, es excesivo. Mi respuesta está destinada a mostrar cómo puede hacer esto en un solo comando sin complementos, sin sentido si solo necesita hacer esto una vez, y cómo puede hacer un comando simple con parámetros si necesita hacerlo. a menudo.
JoL
@Hotschke Para responder a su pregunta, solía instalar todos los complementos bajo el sol que parecían remotamente geniales, pero luego mi vim era increíblemente lenta (lea "un poco lento", lo que es intolerable para un editor). Al analizar más los documentos vim, me di cuenta de que realmente no necesitaba los complementos. Muchas de las características de stock eran lo suficientemente buenas y, para aquellos que no tenían, el shell era el camino a seguir. Fundamentalmente (ignorando las excepciones que se hacen), según la filosofía de Unix, vim es un editor que interactúa bien con otras herramientas del sistema operativo. Creo que esa es la mejor manera de utilizarlo. No hay complementos desde entonces.
JoL
2

Si las columnas están alineadas correctamente, esto se puede hacer con un simple revestimiento.

  1. primero seleccione la columna en modo visual de bloques como lo han demostrado otras respuestas -> CTRL-V+ mueva el cursor
  2. tirar de la selección con y
  3. tipo: :echo eval(join(split(@", '\_s\+'), '+'))que divide el texto extraído en espacios y líneas nuevas, une el elemento con +carácter y evalúa la cadena.
  4. Otra forma de proceder: reemplazar las nuevas líneas con +y evaluar: :echo eval(substitute(@", "\n", '+', 'g'))- eval()es lo más parecido a lo reduceque tenemos.

Si no, tendrás que usar otros trucos para contar campos. Por ejemplo, split(getline('.'), "[ \t|]\\+")se puede usar para dividir las columnas de una fila en su matriz. A partir de ahí, se vuelve tan simple como:

  1. selecciona tus líneas en modo visual
  2. :echo eval(join(map(getline("'<", "'>"), { -> split(v:val, "[ \t|]\\+")[2] }), '+'))

Para deshacerse de los valores mágicos (campo número - 1, y +), puede convertirse en un comando

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

Que se puede usar con:

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

Nota: Aquí uso lambdas de Vim 7.4.1xxx

Luc Hermitte
fuente
1

vmap ++del complemento vmathde Damian Conway

  1. Instale el complemento desde github (solo 178 sloc), por ejemplo

    $ wget https://raw.githubusercontent.com/thoughtstream/Damian-Conway-s-Vim-Setup/master/plugin/vmath.vim -P ~/.vim/pack/manual/start/damians-tools/plugin
    
  2. Agregue mapeo a su vimrc

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

    Sin embargo, sugeriría usar otra cosa, por ejemplo gA

  3. Muévase a la tercera columna 2f|y seleccione la columna en modo de bloque visual<C-V>G$
  4. Presione ++(o su mapeo elegido)
  5. Los resultados se muestran y almacenan en registros (suma en s)
  6. Insertar suma del registro s, por ejemplo, con"sp

Para una presentación de este complemento, vea el video de YouTube Damian Conway, "Más instantáneamente mejor Vim" - OSCON 2013 (a partir del minuto 29).

Hotschke
fuente
1

Herramienta externa de cli csvstatde csvkit

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

Breve explicación de las opciones.

  • -d DELIMITERCarácter delimitador del archivo CSV de entrada. aquí| .
  • -H Especifique que el archivo CSV de entrada no tiene una fila de encabezado.
  • -c COLUMNSUna lista separada por comas de índices de columna o nombres para ser examinados. El valor predeterminado es todas las columnas.
  • --sum Solo sumas de salida.

Esta herramienta también proporciona min, max, mean, median, stdev (desviación estándar), cuenta valores únicos, lista de valores frecuentes.

Insertar en archivo con

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

Instalación

En macOS, csvkit está disponible a través de homebrew y en Debian / Ubuntu y similares con los que se puede instalar $ sudo apt install csvkit.

Hotschke
fuente