duda map dentro de vector (C++)

LioNHearT

buenas tardes a todos.

tengo la siguiente duda: dispongo de un map (string, int), el cual iré almacenando dentro de un vector con diversos valores (es decir, en cada posicion del vector irá una palabra y un numero). como debo hacerlo para poder acceder a dichos valores? ya que he intentado esto pero no funciona:

vector [ n [i->second] - En este caso intento acceder al segundo valor del map situado en la posicion n del vector.

vector[k]=freq - intento guardar en la posicion k del vector, el map freq.

vector [fin+1 [i->second] - intento mirar la segunda posicion del map situado en la posicion fin+1 del vector.

luego otra cuestion. dicho vector luego lo tengo que ordenar. que me aconsejais utilizar un vector o una lista?

disculpad si no me explico bien. si teneis cualquier duda decírmelo.

Gracias de antemano. Espero vuestra ayuda :)

dagavi

Si en cada posición del vector solo va una palabra usar map es complicarte la vida.

Usa pair o créate un struct con esos 2 valores y lo usas de forma normal y corriente.

Igualmente, usando la guía de referencia ya que no he trabajado con map en c++, te puedo decir que:

vector [ n [i->second]
A eso le falta cerrar un corchete: vector [ n ] [i->second]
En ese momento vector[ n ] es un mapa, lo que no entiendo es el segundo corchete que pones "i->second" ya que en ningún momento dices que es "i", independientemente de lo que sea, el corchete de un mapa contiene la clave, y esta función retorna el valor asociado a esa clave.

map<char, string> mapa;
mapa['a'] = "Hola"
cout << mapa['a']; // escribe Hola

Da la impresión por lo que dices aquí "vector[k]=freq - intento guardar en la posicion k del vector, el map freq." que lo que guardas son palabra-frecuencia, 1 por posición del vector, lo que reitero mi primera línea, para eso mejor crear tu propio tipo (ej struct PalabraFrec {string pal, int frec}; ) o usar otro más adecuado como pair. Además parece que lo tratas como si no conocieras las claves, por lo que con más razón no deberías usar map ya que por ejemplo dices:

acceder al segundo valor del map situado en la posicion n del vector.
v[n][??????]
Puedes usar el iterador y acceder al único elemento que haya v[n].begin()->second
Pero eso no tiene mucho sentido pudiendo hacer
v[n].frec con un struct

bLaKnI

No entiendo absolutamente nada de lo que planteas.

Un map se usa para lo que se usa.
Y un vector, para lo suyo tambien.

Porque vas a queres meter un map en un Vector!?

Vector:

  • Estructura posicional tipo array monovalor.

Map:

  • Estructura posicional tipo Hash bivalor. Par Clave - Valor.
    Usa Pair para meter o recoger.

No mezcles cosas.

Especifica exactamente QUE quieres hacer, no como quieres hacerlo o has intentado hacerlo.
Entonces intentaremos echarte un cable! ;)

Insisto:

  • QUE quieres hacer exactamente? Olvida el COMO.

Saludos.

LioNHearT

jejeje ok. lo que quieor hacer es, dado un documento de texto, leer todas las palabras y almacenar su frecuencia de aparicion (osea, cuantas veces aparece cada palabra). entonces, el map lo necesito para guardar, en un lado la palabra, y en el otro la frecuencia de aparicion , osea sera un map del tipo <string,int>. luego el vector lo quiero para guardar los maps, cada uno con su palabra, y despues poder trabajar con el (ordenarlo, mostrar las palabras mas frecuentes, etc.). haber si ahora me explico mejor jejeje.

luego lo de i->second, con esto, lo que hago (o segun pone en mis apuntes xD), es, dado un iterador i (que ja he definido previamente), accedo a donde este apunta que, en este caso, apunta a la 2ª posicion del map, es decir, al valor int.

ahora me entendeis? haber si ahora me explico algo mejor.

espero vuestra respuesta y gracias d nuevo!!!!!

dagavi

Me remito otra vez a #2

Yo he tenido que hacer un proyecto de clustering de documentos en el que una de las partes debía de tratar con la representación del documento que era [palabra, frec]

Lo tienes mal planteado como dije en #2, o usas el map (y solo el map) para representar el documento con todas sus palabras, o usas el vector con otra clase que tenga más sentido usar como puede ser usar "pair" o crear tu propio struct/class como dije antes.
Hacer un vector de maps que solo contienen 1 elemento no tiene ningún sentido, te complicas mucho la programación y lo haces ineficiente.

El vector de maps podría tener sentido si tuvieras un documento en un map y en el vector, en cada posición, un map, por lo tanto tendrías la representación de varios documentos, pero no es el caso.

Pero en este último mensaje dices "en mis apuntes" así que puede que estés haciendo un trabajo para algo de estudios. ¿Realmente te pide que tengas que hacer un map de 1 solo elemento? Ya que el map te puede servir para representar todo el documento. Si no te lo pide/no es un trabajo, simplemente repetiría la respuesta #2.

LioNHearT

no no. en cada posicion del vector irá una palabra y un numero, osea las 2 partes del map.lo explicare como si fueran carpetas. imaginate que tengo un vector/lista llamado C, y dentro un map con 2 posiciones <string, int>. entonces, supongamos que quiero acceder al vector 2:

C[2]= (aqui dentro estará el map con sus 2 valores).

luego, si quisiera acceder al valor int de dicho map, dicho como en carpetas seria como hacer:

C[2].(valor int del map).

nose si ahora me explico. osea dentro de cada posicion del vector (si quieres lo hago con una lista, viene a ser lo mismo), habrán 2 posiciones, que cada una corresponderá a 1 posicion del map.

haber si ahora me explico mejor. y mil gracias tio, que es lo unico que me falta del proyecto y me estoy volviendo loco ya xDD

LioNHearT

mira he hecho un cambio. voy a utilizar la clase pair en vez d la map, ya que veo que mem es mas facil a la hora de trabajar con ella. ya que ahora si puedo hacer:

freq_paraules.first=n;
freq_paraules.second++;

es decir, el 1º campo guardara la variable n (que es un string previamente introducido), y el 2º campo se incrementará en 1. haber si ahora me explico mejor jejeje. ahora solo me falta guardar todos los pair. como me recomiendas que los guarde? porque mas tarde los ordenaré, por eso necesito todos y alomejor un vector no es la mejor opcion...

espero tu respuesta. graciasss

dagavi

El que creo que no se explica soy yo.

Cuando digo un map con 1 valor implícitamente quiero decir 2 valores ya que un map contienes pares clave-valor

Lo que te estoy diciendo es que, a no ser que te obliguen a hacer esa aberración, no debes usar un vector de maps para tener un vector de pares <string, int>

[(Hola, 3), (Adios, 3), (Juan, 38), (Sex, 38487394)]

Para crear esa misma estructura no deberías usar unvector de map, si no otras alternativas como sería crear:

struct PalabraFrec {
    string palabra;
    int frec;
};

vector<PalabraFrec> documento

Con eso ya tienes lo mismo, que quiero sacar todas las frecuencias de cada palabra:

for (int i = 0; i < documento.size(); ++i)
    cout << documento[i].palabra << " - " << documento[i].frec << endl;

Que te obligan a usar un map, pues el vector no tiene sentido ya que un map es, como te han dicho, un contenedor que agrupa pares Clave-Valor usando estructuras como podrían ser tablas hash. Un map puee contener directamente el documento ya que puedes asociar a cada palabra su frecuencia en 1 único map:

map<string, int> documento;
documento["hola"] = 3; documento["adios"] = 3; documento["Juan"] = 38; documento["sex"] = 38487394;

Aquí tienes un programa para que veas un uso tal y como tu lo necesitas, es decir, leyendo palabras e incrementando la frecuencia, de map: http://pastebin.com/m1f666784

dagavi

Una vez "solucionado" la forma de guardar el documento xD A la hora de mantenerlo ordenado pues yo siempre he usado vectores si a posteriori quiero ordenar, si uso listas y lo necesito ordenado por lo general lo voy manteniendo ordenado en la propia lista.
Eso ya no se como te irá mejor. El vector te dará acceso directo y ordenaciones "sencillas" (gracias al acceso directo), la lista podrás mantener de forma sencilla las palabras ordenadas todo el rato (ya que insertar en medio es sencillo y poco costoso en una lista) sin embargo si ordenas a posteriori pues supongo que será más complicado aunque ya te digo que nunca he probado a ordenar listas (siempre las mantenía ordenadas).

Yo en mi proyecto que tenía que representar noticias lo hicimos con un map de string, integer, sin embargo esta información nos la daba un filtro (creado por otro grupo de trabajo) y nos era devuelta en un ArrayList<PalabraFrec> que sería el símil a un vector del struct que te describí en el mensaje anterior o a usar un vector de pair, todo el rato usando add (push_back()) y va bastante bien xD Es decir, a lo mejor no te va mal crear un vector, ir haciendo push_backs según lo necesites y al final ordenar.

LioNHearT

vale, ya he entendido lo del map jeje. guardo las palabras y sus frecuencias y estos los voy colocando en un vector:

    int cont = freq_paraules[n];
    freq_paraules[n] = ++cont;
    llista[k]=freq_paraules;
    k++;

esto es un bucle durante todo el documento hasta que llegue al final. ahora viene la otra duda. como puedo comparar el valor de cont de la posicion k con la posicion k+1 ? digo esto para poder ordenar todo el vector de mayor a menor, porque he de mostrar las 25 palabras mas frecuentes. utilizo el metodo de la burbuja, pero no consigo "acceder" a dicho valor.

hice este algorismo pero lo que te digo, me falla a la hora de acceder a la frecuencia.

for(int in=0 ; in<llista.size() ; in++){
for (int fi=0 ; fi<llista.size()-1; fi++){
if (llista [fi] [i->second] < llista [fi+1] [i->second] )
copia=llista[fi];
llista[fi]=llista[fi+1];
llista[fi+1]=copia;

enga plis que ya me queda poco para dejar de darte la brasa jeejej :)

dagavi

Como dijiste antes que lo vas a hacer con pair supondré que al final la estructura que usas es:

Documento: vector<Pair<string, int>>
O en su defecto, pero que es igual:
Documento: vector<PalabraFrec>, siendo PalabraFrec struct{ string pal; int frec; };

Ya que como hemos dicho no tiene sentido tener un vector de maps y un map no tiene porque ofrecer orden (si usa tablas hash).

Pero una vez tenemos uno de los vectores arriba dichos no tiene mayor problema que definir que sinigifica que una posición sea mayor a otra.

Ordenando por frecuencia pues tan simple como (usando pair)

int n = lista.size() - 1;
bool ordenado = false;
for (int i = 0; not ordenado and i < n; ++i) {
    ordenado = true;
    for (int c = n; c >= 0; --c) {
        if (lista[c].second > lista[c - 1].second) {
            ordenado = false;
            swap(lista[c], lista[c - 1]);
        }
    }
}

Como ves no tiene nada de especial este código para tu programa ya que es el bubble sort pero simplemente se cambia el criterio de ordenación (que es lo que siempre suele cambiar), no tiene ningún misterio, el problema lo tenías en definir mal la estructura de datos que representa un documento, no tenía sentido usar un vector de maps y que cada map solo contenga un par <string, int>

con struct sería lo mismo y además podrías hasta sobrecargar el operador "<" por lo que podrías decir directamente lista < lista[c-1] pero esto ya son cosas para "decorarlo" así que no hace falta que te líes.

LioNHearT

vale. al final lo he usado con pair tal y como te dije. bueno voy a ver si consigo meter el archivo y ahora te cuento.

1000 gracias!

LioNHearT

bueno, de momento me peta el programa xD. mira, he hecho esta funcion que lo que hace es, dado un texto, lo pasa a minusculas y luego guardamos lo valores como dijimos antes. haber que te parece...

fstream conversio("Pride_and_Prejudice.txt");
conversio >> N;
while (!conversio.eof()) {
if(N<=65 || N>=90){
N=N+32;
}
conversio >> N;
}
ifstream analitza("Pride_and_Perjudice.txt");
analitza >> n;
while (!analitza.eof()) {
if(n!=" "){
freq_paraules.first=n;
freq_paraules.second++;
llista[k]=freq_paraules;
k++;
}
analitza >> n;
}

espero tu respuesta.. si no te has cansado ya :$:$

dagavi

La STD al leer strings completos se encarga de quitar espacios, un esquema que podrías seguir es:

string pal;
while (entrada >> pal) {
    minusculas(pal); // Transforma la palabra a minúsculas
    agregar(pal, v); // V es el vector
}

void agregar(string p, vector v) {
    Si la palabra está en V se incrementa su frec
    si no se añade con frecuencia 1
}

Tu haces algo raro:
freq_paraules.first=n;
freq_paraules.second++;
llista[k]=freq_paraules;
k++;

Vas posición por posición (recorriendo el vector) y pones la palabra y incrementas una frecuencia, finalmente metes frec_paraules en el vector y pasas a la siguiente posición:
Si se supone que vas recorriendo el vector por cada palabra que lees del documento significa que tendrás un vector con todo el documento y todo a frecuencias 1.

LioNHearT

lo que hago es la palabra leida la guardo en la 1ª posicion, y despues le sumo uno a la frecuencia, y luefo, esto lo guardo en un vector. no entiendo muy bien como tu me dices... podrias explicarmelo plis :$?

edit: ten en cuenta que tengo un archivo de texto, que es el que tengo que analizar y pasar a minuscula previamente

dagavi

Si la palabra leída la guardas en el vector, implica que las palabras que se lean más de una vez (frecuencia mayor a 1) se repetirán en el vector más de 1 vez, todas ellas con frecuencia 1.

Por otro lado si la añades al vector se supone que la palabra empieza con frecuencia 1, por lo tanto es un riesgo sumarle 1 a la frecuencia ya que no puedes asegurar que un valor que no as asignado sea 0. Si lo añades en vez de ++ debería ser frec = 1; (esto depende más del compilador, pero en cualquier 1ª asignatura de programación te lo dirían como una regla básica "no usarás una variable no inicializada")

Aquí un programa que saca la info de palabras-frec del texto de un archivo:
http://pastebin.com/m764638d4

Supongo que se podría optimizar cambiando la filosofía de trabajo, si cogiéramos un vector de strings y ordenáramos, una vez ordenado el vector (que podría ser coste (-)(n*ln(n))) se podría recorrer 1 vez para obtener el vector de palabra-frec con coste (-)(n)

LioNHearT

esta noche lo pruebo haber que tal va y te comento ok?? gracias tio, eres un crack

bLaKnI

Estas haciendo "Linguistica Computacional"?
Has probado Python?

Yo he hecho todo lo que tu estas haciendo en Python en -1 minuto :P (con cariño lo digo).

Sobre lo que quieres hacer es muy facil.
Pseudocodigo:

  • Lees palabra fichero.
  • Usas palabra como key en el map.
  • Si la key existe, obten par del hash i incrementa su valor i actualizalo reinsertandolo si es necesario.
  • Sino, crea un par nuevo con key - valor=1, y lo insertas al map.

Con esto guardas.

Y la obtencion, sencillamente ya sabes las palabras que hay dentro, osea, accede por keys map['clave'].
En caso de querer recorrerlo TODO entero, usa iteradores.

Nada difícil! :)

Saludos!

PD: releyendo el post, creo que tienes un buen "picha-lio" mental sobre como funcionan las estructuras en general.

Lo que tu quieres hacer es MUY FACIL!
En serio tio...

Se trata de ir palabra por palabra del texto, formateandola como te de la gana, y posteriormente usandola como key para comprobar si existe o no en el hash (map). Si existe, hay que incrementar valor. Sino, creas nuevo valor y padentro!

Mira, ahora te hago el programita y te lo adjunto!

bLaKnI

Bueno, yo lo que te tengo aquí, es un programita pequeño que te lee un fichero, te almacena la frequencia en un Map, y tienes alguna funcion para listar contenido y tal.

Di que mas quieres, y se le añade lo que quieras.

:)

.cpp

#include "UsoMaps.h"


void main(){
	calcFreqWords("textoPrueba1.txt");
	printContenidoMap();

cout << endl;
}

.h

#ifndef __USOMAPS_H__
#define __USOMAPS_H__

#include <iostream>
#include <fstream>
#include <map>
#include <string>

using namespace std;

map<string, int> apariciones; //creamos el map principal.

void calcFreqWords(string FileName){

ifstream inFile;
char word[30]; //define aqui la longitud mas o menos maxima de una palabra.

inFile.open(FileName.c_str());
if (!inFile) {
    cout << "No existe el fichero.";
    exit(1);
}

//bucle principal. Se van leyendo las palabras 1 a 1, y se omiten los espacios y carácteres de salto.
while (inFile >> word){
	++apariciones[word]; //se añade la palabra i se autoincrementa en caso de existir.
}

inFile.close();
}

void printContenidoMap(){
	cout << "Contenido del Map:\n" << endl;
	for(map<string,int>::iterator it = apariciones.begin(); it!=apariciones.end(); it++){
		cout << it->first << " -- " << it->second << endl;
	}
}

#endif 

Ficherillo de pruebas:

Hola
Adios
Hola
Adios
Adios
QueMeLol

Resultados visuales:

Te adjunto el paquetillo de material si quieres.

http://www.megaupload.com/es/?d=Z90XK1WU

los dos archivillos de codigo y algun input.
Compilalo y cambia el texto por el que quieras en el main. Añade si quieres args[] para pasarlo por parametro desde CMD.

edit: xDDD
No me lo hagas escribir en Python Soleil... ^^

edit2: usando la frase de Soleil:

Soleil

El ejercicio resuelto... pero en el lenguaje Scheme : -P

(PD: Sí, me aburro)

;; Hashtable
(define frec (make-hash))

;; Contamos frecuencia
(let ((palabras (regexp-split " "
       (port->string (open-input-file "palabras.txt")))))
       (for-each (lambda (x)
          (if (hash-ref frec x #f)
             (hash-set! frec x  (+ (hash-ref frec x) 1))
             (hash-set! frec x 1))) palabras))

;; Imprimimos
(hash-for-each frec
 (lambda (x y) (printf "palabra: ~s frecuencia: ~s ~c" x y #\Newline)))

Si palabras.txt contiene:
"hola mundo colegas mundo hola"

El programa devuelve:
palabra: "mundo" frecuencia: 2
palabra: "hola" frecuencia: 2
palabra: "colegas" frecuencia: 1

Un saludo : -D

Edit:
Ale yo también pongo foto
http://img119.imageshack.us/img119/4489/mzschemeok8.png

Soleil

Otra versión del programa.
Hace exactamente lo mismo, pero es más eficiente, y más corto. : -)

(hash-for-each
  (let ((palabras (regexp-split " " (port->string 
         (open-input-file "palabras.txt")))) (frec (make-hash)))
  (for-each (lambda (x)
    (hash-set! frec x (+ (hash-ref frec x 0) 1))) palabras) frec)
      (lambda (x y) (printf "palabra: ~s frecuencia: ~s ~c" x y #\Newline)))
LioNHearT

tios, sinceramente me dais envidia. me siento inutil :(. porque me deciis que es muy sencillo pero yo no consigo sacarlo. alguno me lo podria pasar? se que pido mucho pero tios, estoy desesperado.

yo lo que hago es guardar cada palabra y su frecuencia en un map, y los map en un vector, el cual luego ordenare para poder mostrar las 25 palabras de mayor frecuencia. os pido mucho si me lo pasais?

1000 gracias de verda. os envio msg privado por si me quereis agregar y asi lo hablamos mejor :)

bLaKnI

Ahora añadire la función de orden.
Solo hay un problema:

  • Los maps por defecto son contenedores automaticamente ordenados por la Key. Con lo que no se puede ordenar por valor, y por lo tanto, no es trivial obtener las palabras mas frequentes. Ademas, si fuera la MAS frequente, un simple algoritmo de busqueda de la palabra mas frequente basta, buscando en los it->second.
    Pero resulta que pueden ser mas de X palabras a obtener, con lo que el algoritmo ya no es trivial.

Lo que intentaré, es obtener un Map swapeado, de tal manera que quedando ya ordenado automaticamente se puedan obtener las palabras de mas frequencia de aparición.

A ver si se me ocurre...

(con lo facil que es el Python... xDDDD)

EDIT:

Ya te lo tengo.
Es una funcion que le pasas por parámetro el numero de palabras que quieres contemplar. Por ejemplo, las 25 que comentabas, o 3 en mi caso de ejemplo que te enseñaré.
Y te las imprime por pantalla.
Si las quieres devolver, crea un vector o algo y guardalas.

No se si podras usar multimaps, supongo que si te dejan trabajar con la libreria Map, será que sí. Lo bueno de estos, es que guardas el contenido del Map en el multimap, pero con el orden cambiado (Key-Val --> Val-Key; manualmente debes hacerlo) y te permite repetir claves, con lo que palabras que aparezcan 2 veces pueden haber muchisimas.
Luego está claro, que se ordena de forma reversa (ya que por defecto lo hace ascendientemente) y de hecho, ni lo ordeno, sencillamente cojo un iterador reverso, es decir, recorro el map a la inversa. Y finalmente, contemplando en todo momento que no "se salga" del map y se provoque overflow, se van mostrando las palabras hasta que o bien se acaba el map, o bien se llega al tope fijado por el parámetro de entrada. Osea, si el map tiene 30 cosas dentro, pero el parametro es 5, pues a 5 palabras parará. Pero si hay 30 cosas y le metes de parámetro 543, pues sencillamente te muestra las 30 que hay y punto.

Y esto es todo. Una funcion mas.

Te la añado y te resubeo el codigo.

Saludos!

Nueva funcion:

void getMasFrequentes(int X){
	multimap<int,string> frequentes; //se crea un multimap, que permite keys repetidas.
	int cont=0;
	
//se recorre el map entero, swapeando los valores e insertandolos en el multimap.
for(map<string,int>::iterator it = apariciones.begin(); it!=apariciones.end(); it++){
	frequentes.insert( pair<int,string>(it->second,it->first) );
}

cout << "\n\nPalabras mas frequentes [" << X << "]:\n" << endl;
//Obtenemos un iterador reverso (para orden descend), dado el orden natural ascend del multimap:
for(multimap<int, string>::reverse_iterator itMM = frequentes.rbegin(); itMM != frequentes.rend() && cont<X; itMM++ ) {
	cout << ">> Apariciones: " << itMM->first << " -- Palabra: " << itMM->second << endl;
	cont++;
}
}

Y la llamo al main así:

getMasFrequentes(3);

ejemplo:

Te dejo el link del codigo aquí:

http://www.megaupload.com/es/?d=MJZU78M9

NOTA: la funcion esta solo tiene en cuenta el numero que le pasas. Osea, si le dices 7, te devuelve las 7 mas frequentes de mas a menos, hasta 7. Pero si por ejemplo tienes la primera que sale 4 veces, la segunda 3, y despues hay 50 palabras que salen 2 veces, solamente tendras la de 4, la de 3, y 5 palabras mas que aparecen 2 veces, que seran las que sean. Ordenadas segun STL, no segun yo.
Podrias modificar la funcion para que te avisara de que hay mas palabras de la misma cantidad de la ultima que muestras, pero que no estas mostrando. Me explico? xDDD

Saludos.

Usuarios habituales

  • bLaKnI
  • LioNHearT
  • Soleil
  • dagavi